Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-01-24 15:33:02 +00:00
parent 36ba0f6636
commit 664a3c2024
47 changed files with 793 additions and 458 deletions

View File

@ -786,7 +786,6 @@ RSpec/FeatureCategory:
- 'ee/spec/models/ee/pages_deployment_spec.rb'
- 'ee/spec/models/ee/preloaders/group_policy_preloader_spec.rb'
- 'ee/spec/models/ee/project_authorization_spec.rb'
- 'ee/spec/models/ee/project_statistics_spec.rb'
- 'ee/spec/models/ee/protected_ref_spec.rb'
- 'ee/spec/models/ee/release_spec.rb'
- 'ee/spec/models/ee/resource_label_event_spec.rb'

View File

@ -123,7 +123,7 @@
{"name":"devise","version":"4.9.3","platform":"ruby","checksum":"480638d6c51b97f56da6e28d4f3e2a1b8e606681b316aa594b87c6ab94923488"},
{"name":"devise-two-factor","version":"4.1.1","platform":"ruby","checksum":"c95f5b07533e62217aaed3c386874d94e2d472fb5f2b6598afe8600fc17a8b95"},
{"name":"diff-lcs","version":"1.5.0","platform":"ruby","checksum":"49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67"},
{"name":"diffy","version":"3.4.2","platform":"ruby","checksum":"36b42ffbe5138ddc56182107c24ad8d6b066ecfd2876829f391e3a4993d89ae1"},
{"name":"diffy","version":"3.4.3","platform":"ruby","checksum":"4264b9e7db00d1cd426fcd32e36565779163cedc2340a95b0e6f025e71f9aaa7"},
{"name":"digest-crc","version":"0.6.5","platform":"ruby","checksum":"5ca456f3352dc5ff17eb95deb3dd5a79dc79f8bf751d8005abca5b7b9b252124"},
{"name":"discordrb-webhooks","version":"3.5.0","platform":"ruby","checksum":"52fba8bce3b08059d4a41a1e73a9a152958e788a9330275450126b44f01c23b1"},
{"name":"docile","version":"1.4.0","platform":"ruby","checksum":"5f1734bde23721245c20c3d723e76c104208e1aa01277a69901ce770f0ebb8d3"},

View File

@ -525,7 +525,7 @@ GEM
railties (~> 7.0)
rotp (~> 6.0)
diff-lcs (1.5.0)
diffy (3.4.2)
diffy (3.4.3)
digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0)
discordrb-webhooks (3.5.0)

View File

@ -123,7 +123,7 @@
{"name":"devise","version":"4.9.3","platform":"ruby","checksum":"480638d6c51b97f56da6e28d4f3e2a1b8e606681b316aa594b87c6ab94923488"},
{"name":"devise-two-factor","version":"4.1.1","platform":"ruby","checksum":"c95f5b07533e62217aaed3c386874d94e2d472fb5f2b6598afe8600fc17a8b95"},
{"name":"diff-lcs","version":"1.5.0","platform":"ruby","checksum":"49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67"},
{"name":"diffy","version":"3.4.2","platform":"ruby","checksum":"36b42ffbe5138ddc56182107c24ad8d6b066ecfd2876829f391e3a4993d89ae1"},
{"name":"diffy","version":"3.4.3","platform":"ruby","checksum":"4264b9e7db00d1cd426fcd32e36565779163cedc2340a95b0e6f025e71f9aaa7"},
{"name":"digest-crc","version":"0.6.5","platform":"ruby","checksum":"5ca456f3352dc5ff17eb95deb3dd5a79dc79f8bf751d8005abca5b7b9b252124"},
{"name":"discordrb-webhooks","version":"3.5.0","platform":"ruby","checksum":"52fba8bce3b08059d4a41a1e73a9a152958e788a9330275450126b44f01c23b1"},
{"name":"docile","version":"1.4.0","platform":"ruby","checksum":"5f1734bde23721245c20c3d723e76c104208e1aa01277a69901ce770f0ebb8d3"},

View File

@ -537,7 +537,7 @@ GEM
railties (~> 7.0)
rotp (~> 6.0)
diff-lcs (1.5.0)
diffy (3.4.2)
diffy (3.4.3)
digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0)
discordrb-webhooks (3.5.0)

View File

@ -1,4 +1,8 @@
{
"AccessLevelInterface": [
"ContainerProtectionAccessLevel",
"ContainerProtectionTagRule"
],
"AlertManagementIntegration": [
"AlertManagementHttpIntegration",
"AlertManagementPrometheusIntegration"

View File

@ -152,10 +152,10 @@ export default {
<template v-if="activePanel">
<div data-testid="active-panel-template" class="gl-flex gl-items-center gl-py-5">
<div class="col-auto">
<img aria-hidden :src="activePanel.imageSrc" />
<img :alt="''" :src="activePanel.imageSrc" />
</div>
<div class="col">
<h4>{{ activePanel.title }}</h4>
<h1 class="gl-heading-2-fixed gl-my-3">{{ activePanel.title }}</h1>
<p v-if="hasTextDetails">{{ details }}</p>
<component :is="details" v-else v-bind="detailProps" />

View File

@ -326,7 +326,7 @@ export default {
return this.glFeatures.notificationsTodosButtons;
},
parentWorkItem() {
return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY)?.parent;
return this.findWidget(WIDGET_TYPE_HIERARCHY)?.parent;
},
parentWorkItemId() {
return this.parentWorkItem?.id;
@ -364,10 +364,10 @@ export default {
return this.workItem.workItemType?.iconName;
},
hasDescriptionWidget() {
return this.isWidgetPresent(WIDGET_TYPE_DESCRIPTION);
return this.findWidget(WIDGET_TYPE_DESCRIPTION);
},
hasDesignWidget() {
return this.isWidgetPresent(WIDGET_TYPE_DESIGNS) && this.$router;
return this.findWidget(WIDGET_TYPE_DESIGNS) && this.$router;
},
showUploadDesign() {
return this.hasDesignWidget && this.workspacePermissions.createDesign;
@ -376,10 +376,10 @@ export default {
return this.hasDesignWidget && this.workspacePermissions.moveDesign;
},
workItemNotificationsSubscribed() {
return Boolean(this.isWidgetPresent(WIDGET_TYPE_NOTIFICATIONS)?.subscribed);
return Boolean(this.findWidget(WIDGET_TYPE_NOTIFICATIONS)?.subscribed);
},
workItemCurrentUserTodos() {
return this.isWidgetPresent(WIDGET_TYPE_CURRENT_USER_TODOS);
return this.findWidget(WIDGET_TYPE_CURRENT_USER_TODOS);
},
showWorkItemCurrentUserTodos() {
return Boolean(this.$options.isLoggedIn && this.workItemCurrentUserTodos);
@ -388,22 +388,22 @@ export default {
return this.workItemCurrentUserTodos?.currentUserTodos?.nodes;
},
workItemAssignees() {
return this.isWidgetPresent(WIDGET_TYPE_ASSIGNEES);
return this.findWidget(WIDGET_TYPE_ASSIGNEES);
},
workItemAwardEmoji() {
return this.isWidgetPresent(WIDGET_TYPE_AWARD_EMOJI);
return this.findWidget(WIDGET_TYPE_AWARD_EMOJI);
},
workItemHierarchy() {
return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY);
return this.findWidget(WIDGET_TYPE_HIERARCHY);
},
workItemNotes() {
return this.isWidgetPresent(WIDGET_TYPE_NOTES);
return this.findWidget(WIDGET_TYPE_NOTES);
},
workItemWeight() {
return this.isWidgetPresent(WIDGET_TYPE_WEIGHT);
return this.findWidget(WIDGET_TYPE_WEIGHT);
},
workItemDevelopment() {
return this.isWidgetPresent(WIDGET_TYPE_DEVELOPMENT);
return this.findWidget(WIDGET_TYPE_DEVELOPMENT);
},
workItemBodyClass() {
return {
@ -415,11 +415,11 @@ export default {
},
workItemLinkedItems() {
return this.workItemType === WORK_ITEM_TYPE_VALUE_EPIC
? this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS) && this.hasLinkedItemsEpicsFeature
: this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS);
? this.findWidget(WIDGET_TYPE_LINKED_ITEMS) && this.hasLinkedItemsEpicsFeature
: this.findWidget(WIDGET_TYPE_LINKED_ITEMS);
},
showWorkItemTree() {
return this.isWidgetPresent(WIDGET_TYPE_HIERARCHY) && this.allowedChildTypes?.length > 0;
return this.findWidget(WIDGET_TYPE_HIERARCHY) && this.allowedChildTypes?.length > 0;
},
titleClassHeader() {
return {
@ -495,7 +495,7 @@ export default {
enableEditMode() {
this.editMode = true;
},
isWidgetPresent(type) {
findWidget(type) {
return this.widgets?.find((widget) => widget.type === type);
},
toggleConfidentiality(confidentialStatus) {

View File

@ -23,6 +23,50 @@ module Types
field :revision, GraphQL::Types::String, null: true, description: 'Revision of the tag.'
field :short_revision, GraphQL::Types::String, null: true, description: 'Short revision of the tag.'
field :total_size, GraphQL::Types::BigInt, null: true, description: 'Size of the tag.'
field :protection,
Types::ContainerRegistry::Protection::AccessLevelType,
null: true,
experiment: { milestone: '17.9' },
description: 'Minimum GitLab access level required to push and delete container image tags. ' \
'If multiple protection rules match an image tag, the highest access levels are applied'
def protection
return unless Feature.enabled?(:container_registry_protected_tags, project)
highest_matching_rule
end
private
def project
object.repository.project
end
def highest_matching_rule
result = ::ContainerRegistry::Protection::TagRule.new
project.container_registry_protection_tag_rules.each do |rule|
next unless Gitlab::UntrustedRegexp.new(rule.tag_name_pattern).match?(object.name)
set_max_access_level(result, rule)
end
result
end
def set_max_access_level(result, rule)
%i[push delete].each do |action|
attribute = :"minimum_access_level_for_#{action}"
result[attribute] = [
# minimum_access_level_for_push_before_type_cast will return the
# enum's numeric value so we can correctly use .max on it.
result.method(:"#{attribute}_before_type_cast").call.to_i,
rule.method(:"#{attribute}_before_type_cast").call
].max
end
end
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
module Types
module ContainerRegistry
module Protection
module AccessLevelInterface
include BaseInterface
field :minimum_access_level_for_delete,
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
null: true,
experiment: { milestone: '17.8' },
description:
'Minimum GitLab access level required to delete container image tags from the container repository. ' \
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
'If the value is `nil`, no minimum access level is enforced. ' \
'Users with the Developer role or higher can delete tags by default.'
field :minimum_access_level_for_push,
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
null: true,
experiment: { milestone: '17.8' },
description:
'Minimum GitLab access level required to push container image tags to the container repository. ' \
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
'If the value is `nil`, no minimum access level is enforced. ' \
'Users with the Developer role or higher can push tags by default.'
end
end
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
module Types
module ContainerRegistry
module Protection
class AccessLevelType < ::Types::BaseObject # rubocop:disable Graphql/AuthorizeTypes -- it inherits the same authorization as the caller
graphql_name 'ContainerProtectionAccessLevel'
description 'Represents the most restrictive permissions for a container image tag'
implements Types::ContainerRegistry::Protection::AccessLevelInterface
end
end
end
end

View File

@ -8,6 +8,8 @@ module Types
description 'A container repository tag protection rule designed to prevent users with a certain ' \
'access level or lower from altering the container registry.'
implements Types::ContainerRegistry::Protection::AccessLevelInterface
authorize :admin_container_image
field :id,
@ -23,26 +25,6 @@ module Types
description:
'The pattern that matches container image tags to protect. ' \
'For example, `v1.*`. Wildcard character `*` allowed.'
# rubocop:disable GraphQL/ExtractType -- These are stored as separate fields
field :minimum_access_level_for_delete,
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
null: true,
experiment: { milestone: '17.8' },
description:
'Minimum GitLab access level required to delete container image tags from the container repository. ' \
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
'If the value is `nil`, the default minimum access level is `DEVELOPER`.'
field :minimum_access_level_for_push,
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
null: true,
experiment: { milestone: '17.8' },
description:
'Minimum GitLab access level required to push container image tags to the container repository. ' \
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
'If the value is `nil`, the default minimum access level is `DEVELOPER`.'
# rubocop:enable GraphQL/ExtractType -- These are stored as user preferences
end
end
end

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
module Integrations
module Base
module CustomIssueTracker
extend ActiveSupport::Concern
include Base::IssueTracker
include HasIssueTrackerFields
class_methods do
def title
s_('IssueTracker|Custom issue tracker')
end
def description
s_("IssueTracker|Use a custom issue tracker as this project's issue tracker.")
end
def help
build_help_page_url(
'user/project/integrations/custom_issue_tracker.md',
s_("IssueTracker|Use a custom issue tracker that is not in the integration list.")
)
end
def to_param
'custom_issue_tracker'
end
end
included do
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
end
end
end
end

View File

@ -0,0 +1,134 @@
# frozen_string_literal: true
module Integrations
module Base
module DiffblueCover
extend ActiveSupport::Concern
class_methods do
def title
'Diffblue Cover'
end
def description
s_('DiffblueCover|Automatically write comprehensive, human-like Java unit tests.')
end
def to_param
'diffblue_cover'
end
def help
s_('DiffblueCover|Automatically write comprehensive, human-like Java unit tests.')
end
def supported_events
[]
end
def diffblue_link
ActionController::Base.helpers.link_to(
s_('DiffblueCover|Try Diffblue Cover'),
'https://www.diffblue.com/try-cover/gitlab/',
target: '_blank',
rel: 'noopener noreferrer'
)
end
end
included do
field :diffblue_license_key,
section: Integrations::Base::Integration::SECTION_TYPE_CONNECTION,
type: :password,
title: -> { s_('DiffblueCover|License key') },
description: -> { s_('DiffblueCover|Diffblue Cover license key.') },
non_empty_password_title: -> { s_('DiffblueCover|License key') },
non_empty_password_help: -> {
s_(
'DiffblueCover|Leave blank to use your current license key.'
)
},
exposes_secrets: true,
required: true,
is_secret: true,
placeholder: 'XXXX-XXXX-XXXX-XXXX',
help: -> {
format(
s_(
'DiffblueCover|Enter your Diffblue Cover license key or ' \
'go to %{diffblue_link} to obtain a free trial license.'
),
diffblue_link: diffblue_link
)
}
field :diffblue_access_token_name,
section: Integrations::Base::Integration::SECTION_TYPE_CONFIGURATION,
title: -> { s_('DiffblueCover|Name') },
description: -> { s_('DiffblueCover|Access token name used by Diffblue Cover in pipelines.') },
required: true,
placeholder: -> { s_('DiffblueCover|My token name') }
field :diffblue_access_token_secret,
section: Integrations::Base::Integration::SECTION_TYPE_CONFIGURATION,
type: :password,
title: -> { s_('DiffblueCover|Secret') },
description: -> { s_('DiffblueCover|Access token secret used by Diffblue Cover in pipelines.') },
non_empty_password_title: -> { s_('DiffblueCover|Secret') },
non_empty_password_help: -> { s_('DiffblueCover|Leave blank to use your current secret value.') },
required: true,
is_secret: true,
placeholder: 'glpat-XXXXXXXXXXXXXXXXXXXX' # gitleaks:allow
with_options if: :activated? do
validates :diffblue_license_key, presence: true
validates :diffblue_access_token_name, presence: true
validates :diffblue_access_token_secret, presence: true
end
end
def avatar_url
ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/diffblue.svg')
end
def sections
[
{
type: Integrations::Base::Integration::SECTION_TYPE_CONNECTION,
title: s_('DiffblueCover|Integration details'),
description:
s_(
'DiffblueCover|Diffblue Cover is a generative AI platform that automatically ' \
'writes comprehensive, human-like Java unit tests. Integrate Diffblue ' \
'Cover into your CI/CD workflow for fully autonomous operation.'
)
},
{
type: Integrations::Base::Integration::SECTION_TYPE_CONFIGURATION,
title: s_('DiffblueCover|Access token'),
description:
'You must have a GitLab access token for Diffblue Cover to access your project. ' \
'Use a GitLab access token with at least the Developer role and ' \
'the <code>api</code> and <code>write_repository</code> permissions.'
}
]
end
def execute(_data) end
def ci_variables
return [] unless activated?
[
{ key: 'DIFFBLUE_LICENSE_KEY', value: diffblue_license_key, public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN_NAME', value: diffblue_access_token_name, public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN', value: diffblue_access_token_secret, public: false, masked: true }
]
end
def testable?
false
end
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
module Integrations
module Base
module Ewm
extend ActiveSupport::Concern
include Base::IssueTracker
include HasIssueTrackerFields
class_methods do
def title
'EWM'
end
def description
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker.")
end
def help
build_help_page_url(
'user/project/integrations/ewm.md',
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker.")
)
end
def to_param
'ewm'
end
end
included do
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def reference_pattern(*)
@reference_pattern ||= %r{(?<issue>\b(?:bug|task|work item|workitem|rtcwi|defect)\b\s+\d+)}i
end
def issue_url(iid)
issues_url.gsub(':id', iid.to_s.split(' ')[-1])
end
end
end
end
end

View File

@ -2,28 +2,6 @@
module Integrations
class CustomIssueTracker < Integration
include Base::IssueTracker
include HasIssueTrackerFields
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def self.title
s_('IssueTracker|Custom issue tracker')
end
def self.description
s_("IssueTracker|Use a custom issue tracker as this project's issue tracker.")
end
def self.help
build_help_page_url(
'user/project/integrations/custom_issue_tracker.md',
s_("IssueTracker|Use a custom issue tracker that is not in the integration list.")
)
end
def self.to_param
'custom_issue_tracker'
end
include Integrations::Base::CustomIssueTracker
end
end

View File

@ -2,125 +2,6 @@
module Integrations
class DiffblueCover < Integration
field :diffblue_license_key,
section: SECTION_TYPE_CONNECTION,
type: :password,
title: -> { s_('DiffblueCover|License key') },
description: -> { s_('DiffblueCover|Diffblue Cover license key.') },
non_empty_password_title: -> { s_('DiffblueCover|License key') },
non_empty_password_help: -> {
s_(
'DiffblueCover|Leave blank to use your current license key.'
)
},
exposes_secrets: true,
required: true,
is_secret: true,
placeholder: 'XXXX-XXXX-XXXX-XXXX',
help: -> {
format(
s_(
'DiffblueCover|Enter your Diffblue Cover license key or ' \
'go to %{diffblue_link} to obtain a free trial license.'
),
diffblue_link: diffblue_link
)
}
field :diffblue_access_token_name,
section: SECTION_TYPE_CONFIGURATION,
title: -> { s_('DiffblueCover|Name') },
description: -> { s_('DiffblueCover|Access token name used by Diffblue Cover in pipelines.') },
required: true,
placeholder: -> { s_('DiffblueCover|My token name') }
field :diffblue_access_token_secret,
section: SECTION_TYPE_CONFIGURATION,
type: :password,
title: -> { s_('DiffblueCover|Secret') },
description: -> { s_('DiffblueCover|Access token secret used by Diffblue Cover in pipelines.') },
non_empty_password_title: -> { s_('DiffblueCover|Secret') },
non_empty_password_help: -> { s_('DiffblueCover|Leave blank to use your current secret value.') },
required: true,
is_secret: true,
placeholder: 'glpat-XXXXXXXXXXXXXXXXXXXX' # gitleaks:allow
with_options if: :activated? do
validates :diffblue_license_key, presence: true
validates :diffblue_access_token_name, presence: true
validates :diffblue_access_token_secret, presence: true
end
def self.title
'Diffblue Cover'
end
def self.description
s_('DiffblueCover|Automatically write comprehensive, human-like Java unit tests.')
end
def self.to_param
'diffblue_cover'
end
def self.help
s_('DiffblueCover|Automatically write comprehensive, human-like Java unit tests.')
end
def avatar_url
ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/diffblue.svg')
end
def self.supported_events
[]
end
def sections
[
{
type: SECTION_TYPE_CONNECTION,
title: s_('DiffblueCover|Integration details'),
description:
s_(
'DiffblueCover|Diffblue Cover is a generative AI platform that automatically ' \
'writes comprehensive, human-like Java unit tests. Integrate Diffblue ' \
'Cover into your CI/CD workflow for fully autonomous operation.'
)
},
{
type: SECTION_TYPE_CONFIGURATION,
title: s_('DiffblueCover|Access token'),
description:
'You must have a GitLab access token for Diffblue Cover to access your project. ' \
'Use a GitLab access token with at least the Developer role and ' \
'the <code>api</code> and <code>write_repository</code> permissions.'
}
]
end
def execute(_data) end
def ci_variables
return [] unless activated?
[
{ key: 'DIFFBLUE_LICENSE_KEY', value: diffblue_license_key, public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN_NAME', value: diffblue_access_token_name, public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN', value: diffblue_access_token_secret, public: false, masked: true }
]
end
def testable?
false
end
def self.diffblue_link
ActionController::Base.helpers.link_to(
s_('DiffblueCover|Try Diffblue Cover'),
'https://www.diffblue.com/try-cover/gitlab/',
target: '_blank',
rel: 'noopener noreferrer'
)
end
include Integrations::Base::DiffblueCover
end
end

View File

@ -2,36 +2,6 @@
module Integrations
class Ewm < Integration
include Base::IssueTracker
include HasIssueTrackerFields
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
def reference_pattern(only_long: true)
@reference_pattern ||= %r{(?<issue>\b(?:bug|task|work item|workitem|rtcwi|defect)\b\s+\d+)}i
end
def self.title
'EWM'
end
def self.description
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker.")
end
def self.help
build_help_page_url(
'user/project/integrations/ewm.md',
s_("IssueTracker|Use IBM Engineering Workflow Management as this project's issue tracker.")
)
end
def self.to_param
'ewm'
end
def issue_url(iid)
issues_url.gsub(':id', iid.to_s.split(' ')[-1])
end
include Integrations::Base::Ewm
end
end

View File

@ -3,7 +3,7 @@
module Integrations
module Instance
class CustomIssueTracker < Integration
# To be updated as part of https://gitlab.com/gitlab-org/gitlab/-/issues/474809
include Integrations::Base::CustomIssueTracker
end
end
end

View File

@ -3,7 +3,7 @@
module Integrations
module Instance
class DiffblueCover < Integration
# To be updated as part of https://gitlab.com/gitlab-org/gitlab/-/issues/474809
include Integrations::Base::DiffblueCover
end
end
end

View File

@ -3,7 +3,7 @@
module Integrations
module Instance
class Ewm < Integration
# To be updated as part of https://gitlab.com/gitlab-org/gitlab/-/issues/474809
include Integrations::Base::Ewm
end
end
end

View File

@ -3,7 +3,7 @@
= gitlab_ui_form_with url: configure_import_bulk_imports_path(namespace_id: params[:parent_id]), class: 'gl-show-field-errors' do |f|
.gl-border-l-solid.gl-border-r-solid.gl-border-t-solid.gl-border-default.gl-border-1.gl-p-5.gl-mt-4
.gl-flex.gl-items-center.gl-justify-between
%h4.gl-flex
%h2.gl-flex.gl-heading-2-fixed.gl-my-3
= s_('GroupsNew|Import groups by direct transfer')
= render Pajamas::ButtonComponent.new(href: history_import_bulk_imports_path, category: :secondary, variant: :confirm, size: :small) do
= s_('BulkImport|View import history')

View File

@ -4,7 +4,7 @@
= gitlab_ui_form_for '', url: import_gitlab_group_path, namespace: 'import_group', class: 'group-form gl-show-field-errors', multipart: true do |f|
.gl-border-l-solid.gl-border-r-solid.gl-border-default.gl-border-1.gl-p-5
%h4
%h2.gl-heading-2-fixed.gl-my-3
= _('Import group from file')
= render Pajamas::AlertComponent.new(variant: :warning,
alert_options: { class: 'gl-mb-5' },

View File

@ -0,0 +1,51 @@
# frozen_string_literal: true
class CreateSiphonNotes < ClickHouse::Migration
def up
execute <<-SQL
CREATE TABLE IF NOT EXISTS siphon_notes
(
note Nullable(String),
noteable_type LowCardinality(String),
author_id Nullable(Int64),
created_at Nullable(DateTime64(6, 'UTC')),
updated_at Nullable(DateTime64(6, 'UTC')),
project_id Nullable(Int64),
attachment Nullable(String) DEFAULT '',
line_code Nullable(String),
commit_id Nullable(String),
noteable_id Nullable(Int64),
system Bool DEFAULT false,
st_diff Nullable(String),
updated_by_id Nullable(Int64),
type LowCardinality(String) DEFAULT '',
position Nullable(String),
original_position Nullable(String),
resolved_at Nullable(DateTime64(6, 'UTC')),
resolved_by_id Nullable(Int64),
discussion_id Nullable(String),
note_html Nullable(String),
cached_markdown_version Nullable(Int64),
change_position Nullable(String),
resolved_by_push Nullable(Bool),
review_id Nullable(Int64),
confidential Nullable(Bool),
last_edited_at Nullable(DateTime64(6, 'UTC')),
internal Bool DEFAULT false,
id Int64,
namespace_id Nullable(Int64),
imported_from Int8 DEFAULT 0,
_siphon_replicated_at DateTime64(6, 'UTC') DEFAULT now(),
_siphon_deleted Bool DEFAULT FALSE
)
ENGINE = ReplacingMergeTree(_siphon_replicated_at, _siphon_deleted)
PRIMARY KEY id
SQL
end
def down
execute <<-SQL
DROP TABLE IF EXISTS siphon_notes
SQL
end
end

View File

@ -739,16 +739,37 @@ For Linux package (Omnibus):
gitlab_rails['backup_upload_connection'] = {
'provider' => 'AWS',
'region' => 'eu-west-1',
# Choose one authentication method
# IAM Profile
'use_iam_profile' => true
# OR AWS Access and Secret key
'aws_access_key_id' => 'AKIAKIAKI',
'aws_secret_access_key' => 'secret123'
# If using an IAM Profile, don't configure aws_access_key_id & aws_secret_access_key
# 'use_iam_profile' => true
}
gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
# Consider using multipart uploads when file size reaches 100MB. Enter a number in bytes.
# gitlab_rails['backup_multipart_chunk_size'] = 104857600
```
1. If you're using the IAM Profile authentication method, ensure the instance where `backup-utility` is to be run has the following policy set (replace `<backups-bucket>` with the correct bucket name):
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::<backups-bucket>/*"
}
]
}
```
1. [Reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation)
for the changes to take effect
@ -889,57 +910,6 @@ For self-compiled installations:
1. [Restart GitLab](../restart_gitlab.md#self-compiled-installations)
for the changes to take effect
If you're uploading your backups to S3, you should create a new
IAM user with restricted access rights. To give the upload user access only for
uploading backups create the following IAM profile, replacing `my.s3.bucket`
with the name of your bucket:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1412062044000",
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketAcl",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:ListBucketMultipartUploads",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": [
"arn:aws:s3:::my.s3.bucket/*"
]
},
{
"Sid": "Stmt1412062097000",
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
],
"Resource": [
"*"
]
},
{
"Sid": "Stmt1412062128000",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my.s3.bucket"
]
}
]
}
```
##### Using Google Cloud Storage
To use Google Cloud Storage to save backups, you must first create an

View File

@ -230,7 +230,6 @@ To set up an instance profile:
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
@ -238,6 +237,13 @@ To set up an instance profile:
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::test-bucket/*"
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::test-bucket"
}
]
}

View File

@ -3825,8 +3825,8 @@ Input type: `createContainerProtectionTagRuleInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcreatecontainerprotectiontagruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` | [`ContainerProtectionTagRuleAccessLevel!`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. Introduced in GitLab 17.8: **Status**: Experiment. |
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`ContainerProtectionTagRuleAccessLevel!`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. Introduced in GitLab 17.8: **Status**: Experiment. |
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` | [`ContainerProtectionTagRuleAccessLevel!`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can delete tags by default. Introduced in GitLab 17.8: **Status**: Experiment. |
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`ContainerProtectionTagRuleAccessLevel!`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can push tags by default. Introduced in GitLab 17.8: **Status**: Experiment. |
| <a id="mutationcreatecontainerprotectiontagruleprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project containing the container image tags. |
| <a id="mutationcreatecontainerprotectiontagruletagnamepattern"></a>`tagNamePattern` | [`String!`](#string) | The pattern that matches container image tags to protect. For example, `v1.*`. Wildcard character `*` allowed. Introduced in GitLab 17.8: **Status**: Experiment. |
@ -19255,6 +19255,8 @@ Extra metadata for AI message.
### `AiMetrics`
Requires ClickHouse. Premium and Ultimate with GitLab Duo Pro and Enterprise only.
#### Fields
| Name | Type | Description |
@ -22007,6 +22009,17 @@ A tag expiration policy designed to keep only the images that matter most.
| <a id="containerexpirationpolicyolderthan"></a>`olderThan` | [`ContainerExpirationPolicyOlderThanEnum`](#containerexpirationpolicyolderthanenum) | Tags older than the given age will expire. |
| <a id="containerexpirationpolicyupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the container expiration policy was updated. |
### `ContainerProtectionAccessLevel`
Represents the most restrictive permissions for a container image tag.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="containerprotectionaccesslevelminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can delete tags by default. |
| <a id="containerprotectionaccesslevelminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can push tags by default. |
### `ContainerProtectionRepositoryRule`
A container repository protection rule designed to prevent users with a certain access level or lower from altering the container registry.
@ -22029,8 +22042,8 @@ A container repository tag protection rule designed to prevent users with a cert
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="containerprotectiontagruleid"></a>`id` **{warning-solid}** | [`ContainerRegistryProtectionTagRuleID!`](#containerregistryprotectiontagruleid) | **Introduced** in GitLab 17.8. **Status**: Experiment. ID of the container repository tag protection rule. |
| <a id="containerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. |
| <a id="containerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, the default minimum access level is `DEVELOPER`. |
| <a id="containerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can delete tags by default. |
| <a id="containerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can push tags by default. |
| <a id="containerprotectiontagruletagnamepattern"></a>`tagNamePattern` **{warning-solid}** | [`String!`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. The pattern that matches container image tags to protect. For example, `v1.*`. Wildcard character `*` allowed. |
### `ContainerRepository`
@ -22174,6 +22187,7 @@ A tag from a container repository.
| <a id="containerrepositorytagmediatype"></a>`mediaType` | [`String`](#string) | Media type of the tag. |
| <a id="containerrepositorytagname"></a>`name` | [`String!`](#string) | Name of the tag. |
| <a id="containerrepositorytagpath"></a>`path` | [`String!`](#string) | Path of the tag. |
| <a id="containerrepositorytagprotection"></a>`protection` **{warning-solid}** | [`ContainerProtectionAccessLevel`](#containerprotectionaccesslevel) | **Introduced** in GitLab 17.9. **Status**: Experiment. Minimum GitLab access level required to push and delete container image tags. If multiple protection rules match an image tag, the highest access levels are applied. |
| <a id="containerrepositorytagpublishedat"></a>`publishedAt` | [`Time`](#time) | Timestamp when the tag was published. |
| <a id="containerrepositorytagreferrers"></a>`referrers` | [`[ContainerRepositoryReferrer!]`](#containerrepositoryreferrer) | Referrers for the tag. |
| <a id="containerrepositorytagrevision"></a>`revision` | [`String`](#string) | Revision of the tag. |
@ -43725,6 +43739,20 @@ One of:
### Interfaces
#### `AccessLevelInterface`
Implementations:
- [`ContainerProtectionAccessLevel`](#containerprotectionaccesslevel)
- [`ContainerProtectionTagRule`](#containerprotectiontagrule)
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="accesslevelinterfaceminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can delete tags by default. |
| <a id="accesslevelinterfaceminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` **{warning-solid}** | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no minimum access level is enforced. Users with the Developer role or higher can push tags by default. |
#### `AlertManagementIntegration`
Implementations:

View File

@ -10,14 +10,14 @@ module Gitlab
def initialize(project, options = nil)
@project = project
@project_namespace, _, @project_path = project.full_path.downcase.partition('/')
namespace, _, @project_path = project.full_path.partition('/')
@project_namespace = namespace.downcase
@options = options || {}
end
def pages_url
default_url
.to_s
.downcase
end
def unique_host
@ -34,7 +34,7 @@ module Gitlab
# See https://docs.gitlab.com/ee/user/project/pages/getting_started_part_one.html#user-and-group-website-examples.
def is_namespace_homepage? # rubocop:disable Naming/PredicateName -- namespace_homepage is not an
# adjective, so adding "is_" improves understandability
project_path == "#{project_namespace}.#{instance_pages_domain}"
project_path.downcase == "#{project_namespace}.#{instance_pages_domain}"
end
def artifact_url(artifact, job)

View File

@ -3099,6 +3099,9 @@ msgstr ""
msgid "Action not allowed."
msgstr ""
msgid "Action required: %{project_name} is read-only"
msgstr ""
msgid "Action to take when receiving an alert. %{docsLink}"
msgstr ""
@ -24742,6 +24745,12 @@ msgstr ""
msgid "For investigating IT service disruptions or outages"
msgstr ""
msgid "For more information %{support_link_start}contact support%{link_end}."
msgstr ""
msgid "For more information contact support: %{support_link}."
msgstr ""
msgid "For more information on how the number of active users is calculated, see the %{self_managed_subscriptions_doc_link} documentation."
msgstr ""
@ -59083,6 +59092,9 @@ msgstr ""
msgid "To remove the %{link_start}read-only%{link_end} state and regain write access, you can reduce the number of users in your top-level group to %{free_limit} users or less. You can also upgrade to a paid tier, which do not have user limits. If you need additional time, you can start a free 60-day trial which includes unlimited users."
msgstr ""
msgid "To remove the read-only state, reduce git repository and git LFS storage."
msgstr ""
msgid "To resolve the conflicts, either use interactive mode to select %{use_ours} or %{use_theirs}, or edit the files inline. Commit these changes into %{branch_name}."
msgstr ""
@ -65852,6 +65864,12 @@ msgstr ""
msgid "You have been unsubscribed from this thread."
msgstr ""
msgid "You have consumed all available storage and you can't push or add large files to %{project_name}"
msgstr ""
msgid "You have consumed all available storage and you can't push or add large files to %{strong_start}%{project_name}%{strong_end}"
msgstr ""
msgid "You have declined the invitation to join %{title} %{name}."
msgstr ""

View File

@ -65,7 +65,7 @@
"@gitlab/ui": "107.0.1",
"@gitlab/vue-router-vue3": "npm:vue-router@4.1.6",
"@gitlab/vuex-vue3": "npm:vuex@4.0.0",
"@gitlab/web-ide": "^0.0.1-dev-20250109231656",
"@gitlab/web-ide": "^0.0.1-dev-20250110172049",
"@gleam-lang/highlight.js-gleam": "^1.5.0",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
"@rails/actioncable": "7.0.807",

View File

@ -252,14 +252,27 @@ module QA
within_vscode_editor do
# VSCode eagerly removes the input[type='file'] from click on Upload.
# We need to execute a script on the iframe to stub out the iframes body.removeChild to add it back in.
page.execute_script("document.body.removeChild = function(){};")
page.execute_script(
<<~JAVASCRIPT
window.__gl_old_remove = HTMLInputElement.prototype.remove;
HTMLInputElement.prototype.remove = function(){};
JAVASCRIPT
)
# under some conditions the page may not be fully loaded and the right click
# context menu can get closed prior to hitting 'upload' leading to failures
Support::Retrier.retry_until(retry_on_exception: true, message: "Uploading a file in vscode") do
right_click_file_explorer
click_upload_menu_item
enter_file_input(file_path)
begin
# under some conditions the page may not be fully loaded and the right click
# context menu can get closed prior to hitting 'upload' leading to failures
Support::Retrier.retry_until(retry_on_exception: true, message: "Uploading a file in vscode") do
right_click_file_explorer
click_upload_menu_item
enter_file_input(file_path)
end
ensure
page.execute_script(
<<~JAVASCRIPT
HTMLInputElement.prototype.remove = window.__gl_old_remove;
JAVASCRIPT
)
end
end
end
@ -339,6 +352,12 @@ module QA
end
def right_click_file_explorer
# NOTE: Web IDE prompts for clipboard permission to open the file explorer context menu
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/177778#note_2295036716
# https://gitlab.com/gitlab-org/gitlab-web-ide/-/issues/433
page.driver.browser.add_permission("clipboard-read", "granted")
page.driver.browser.add_permission("clipboard-write", "granted")
page.find('.explorer-folders-view', visible: true).right_click
end

View File

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['ContainerRepositoryTag'], feature_category: :container_registry do
fields = %i[name path location digest revision short_revision
total_size created_at user_permissions referrers published_at media_type]
total_size created_at user_permissions referrers published_at media_type protection]
it { expect(described_class.graphql_name).to eq('ContainerRepositoryTag') }

View File

@ -103,6 +103,12 @@ RSpec.describe Gitlab::Pages::UrlBuilder, feature_category: :pages do
it { is_expected.to eq('http://unique-domain.example.com') }
end
end
context 'when the project path contains capitals' do
let(:full_path) { 'group/MyProject' }
it { is_expected.to eq('http://group.example.com/MyProject') }
end
end
context 'when namespace_in_path is true' do

View File

@ -3,29 +3,5 @@
require 'spec_helper'
RSpec.describe Integrations::CustomIssueTracker, feature_category: :integrations do
describe 'Validations' do
context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
it_behaves_like 'issue tracker integration URL attribute', :project_url
it_behaves_like 'issue tracker integration URL attribute', :issues_url
it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
context 'when integration is inactive' do
before do
subject.active = false
end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:issues_url) }
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
end
it_behaves_like Integrations::Base::CustomIssueTracker
end

View File

@ -3,72 +3,5 @@
require 'spec_helper'
RSpec.describe Integrations::DiffblueCover, feature_category: :integrations do
let_it_be(:project) { build(:project) }
subject(:integration) { build(:diffblue_cover_integration, project: project) }
describe 'Validations' do
context 'when active' do
before do
integration.active = true
end
it { is_expected.to validate_presence_of(:diffblue_license_key) }
it { is_expected.to validate_presence_of(:diffblue_access_token_name) }
it { is_expected.to validate_presence_of(:diffblue_access_token_secret) }
end
context 'when inactive' do
before do
integration.active = false
end
it { is_expected.not_to validate_presence_of(:diffblue_license_key) }
it { is_expected.not_to validate_presence_of(:diffblue_access_token_name) }
it { is_expected.not_to validate_presence_of(:diffblue_access_token_secret) }
end
end
describe '#avatar_url' do
it 'returns the avatar image path' do
expect(integration.avatar_url).to eq(ActionController::Base.helpers.image_path(
'illustrations/third-party-logos/integrations-logos/diffblue.svg'
))
end
end
describe '#ci-vars' do
let(:ci_vars) do
[
{ key: 'DIFFBLUE_LICENSE_KEY', value: '1234-ABCD-DCBA-4321', public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN_NAME', value: 'Diffblue CI', public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN',
value: 'glpat-00112233445566778899', public: false, masked: true } # gitleaks:allow
]
end
context 'when active' do
before do
integration.active = true
end
it 'returns the required pipeline vars' do
expect(integration.ci_variables).to match_array(ci_vars)
end
end
context 'when inactive' do
before do
integration.active = false
end
it 'does not return the required pipeline vars' do
expect(integration.ci_variables).to be_empty
end
end
end
describe '#diffblue_link' do
it { expect(described_class.diffblue_link).to include("https://www.diffblue.com/try-cover/gitlab/") }
end
it_behaves_like Integrations::Base::DiffblueCover
end

View File

@ -2,56 +2,6 @@
require 'spec_helper'
RSpec.describe Integrations::Ewm do
describe 'Validations' do
context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
it_behaves_like 'issue tracker integration URL attribute', :project_url
it_behaves_like 'issue tracker integration URL attribute', :issues_url
it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
context 'when integration is inactive' do
before do
subject.active = false
end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:issues_url) }
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
end
describe "ReferencePatternValidation" do
it "extracts bug" do
expect(subject.reference_pattern.match("This is bug 123")[:issue]).to eq("bug 123")
end
it "extracts task" do
expect(subject.reference_pattern.match("This is task 123.")[:issue]).to eq("task 123")
end
it "extracts work item" do
expect(subject.reference_pattern.match("This is work item 123 now")[:issue]).to eq("work item 123")
end
it "extracts workitem" do
expect(subject.reference_pattern.match("workitem 123 at the beginning")[:issue]).to eq("workitem 123")
end
it "extracts defect" do
expect(subject.reference_pattern.match("This is defect 123 defect")[:issue]).to eq("defect 123")
end
it "extracts rtcwi" do
expect(subject.reference_pattern.match("This is rtcwi 123")[:issue]).to eq("rtcwi 123")
end
end
RSpec.describe Integrations::Ewm, feature_category: :integrations do
it_behaves_like Integrations::Base::Ewm
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::Instance::CustomIssueTracker, feature_category: :integrations do
it_behaves_like Integrations::Base::CustomIssueTracker
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::Instance::DiffblueCover, feature_category: :integrations do
it_behaves_like Integrations::Base::DiffblueCover
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::Instance::Ewm, feature_category: :integrations do
it_behaves_like Integrations::Base::Ewm
end

View File

@ -131,8 +131,6 @@ RSpec.describe 'container repository details', feature_category: :container_regi
end
end
it_behaves_like 'returning proper responses with different permissions', raw_tags: -> { tags }
context 'with a giant size tag' do
let(:tags) { %w[latest] }
let(:giant_size) { 1.terabyte }
@ -217,6 +215,10 @@ RSpec.describe 'container repository details', feature_category: :container_regi
GQL
end
before do
stub_container_registry_gitlab_api_support(supported: false)
end
it 'sorts the tags', :aggregate_failures do
subject
@ -385,7 +387,15 @@ RSpec.describe 'container repository details', feature_category: :container_regi
it_behaves_like 'handling graphql network errors with the container registry'
context 'when list tags API is enabled' do
context 'when the Gitlab API is not supported' do
before do
stub_container_registry_gitlab_api_support(supported: false)
end
it_behaves_like 'returning proper responses with different permissions', raw_tags: -> { tags }
end
context 'when the Gitlab API is supported' do
before do
stub_container_registry_config(enabled: true)
allow_next_instances_of(ContainerRegistry::GitlabApiClient, nil) do |client|
@ -648,4 +658,79 @@ RSpec.describe 'container repository details', feature_category: :container_regi
expect(migration_state_response).to eq('')
end
end
context 'protection field' do
let(:raw_tags_response) { [{ name: 'latest', digest: 'sha256:123' }] }
let(:response_body) { { response_body: ::Gitlab::Json.parse(raw_tags_response.to_json) } }
let(:query) do
<<~GQL
query($id: ContainerRepositoryID!) {
containerRepository(id: $id) {
tags(first: 5) {
nodes {
protection {
minimumAccessLevelForPush
minimumAccessLevelForDelete
}
}
}
}
}
GQL
end
let(:tag_permissions_response) do
container_repository_details_response.dig('tags', 'nodes')[0]['protection']
end
before_all do
create(
:container_registry_protection_tag_rule,
project: project,
tag_name_pattern: 'latest',
minimum_access_level_for_push: 'maintainer',
minimum_access_level_for_delete: 'owner'
)
create(
:container_registry_protection_tag_rule,
project: project,
tag_name_pattern: '.*',
minimum_access_level_for_push: 'owner',
minimum_access_level_for_delete: 'maintainer'
)
create(
:container_registry_protection_tag_rule,
project: project,
tag_name_pattern: 'non-matching-pattern',
minimum_access_level_for_push: 'admin',
minimum_access_level_for_delete: 'admin'
)
end
it 'returns the maximum access fields from the matching protection rules' do
subject
expect(tag_permissions_response).to eq(
{
'minimumAccessLevelForPush' => 'OWNER',
'minimumAccessLevelForDelete' => 'OWNER'
}
)
end
context 'when the feature container_registry_protected_tags is disabled' do
before do
stub_feature_flags(container_registry_protected_tags: false)
end
it 'returns nil' do
subject
expect(tag_permissions_response).to be_nil
end
end
end
end

View File

@ -39,9 +39,7 @@ RSpec.describe 'Creating a todo for the alert', feature_category: :incident_mana
context 'todo already exists' do
before do
stub_feature_flags(multiple_todos: false)
create(:todo, :pending, project: project, user: user, target: alert)
post_graphql_mutation(mutation, current_user: user)
end
it 'surfaces an error' do

View File

@ -388,9 +388,14 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
end
end
shared_examples 'an issuable' do |issuable_type|
shared_examples 'an issuable' do |param|
let(:issuable_type) { param }
def create_todo_for_issuable(user, iid = issuable.iid)
post api("/projects/#{project_1.id}/#{issuable_type}/#{iid}/todo", user)
end
it 'creates a todo on an issuable' do
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", john_doe)
create_todo_for_issuable(john_doe)
expect(response).to have_gitlab_http_status(:created)
expect(json_response['project']).to be_a Hash
@ -406,11 +411,9 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
end
it 'returns 304 there already exist a todo on that issuable' do
stub_feature_flags(multiple_todos: false)
create_todo_for_issuable(john_doe)
create(:todo, project: project_1, author: author_1, user: john_doe, target: issuable)
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", john_doe)
create_todo_for_issuable(john_doe)
expect(response).to have_gitlab_http_status(:not_modified)
end
@ -418,7 +421,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
it 'returns 404 if the issuable is not found' do
unknown_id = 0
post api("/projects/#{project_1.id}/#{issuable_type}/#{unknown_id}/todo", john_doe)
create_todo_for_issuable(john_doe, unknown_id)
expect(response).to have_gitlab_http_status(:not_found)
end
@ -427,7 +430,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
guest = create(:user)
project_1.add_guest(guest)
post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.iid}/todo", guest)
create_todo_for_issuable(guest)
if issuable_type == 'merge_requests'
expect(response).to have_gitlab_http_status(:forbidden)

View File

@ -58,10 +58,6 @@ RSpec.describe AlertManagement::Alerts::Todo::CreateService, feature_category: :
create(:todo, :pending, **todo_params)
end
before do
stub_feature_flags(multiple_todos: false)
end
it 'does not create a todo' do
expect { result }.not_to change { Todo.count }
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
RSpec.shared_examples Integrations::Base::CustomIssueTracker do
describe 'Validations' do
context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
it_behaves_like 'issue tracker integration URL attribute', :project_url
it_behaves_like 'issue tracker integration URL attribute', :issues_url
it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
context 'when integration is inactive' do
before do
subject.active = false
end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:issues_url) }
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
end
end

View File

@ -0,0 +1,72 @@
# frozen_string_literal: true
RSpec.shared_examples Integrations::Base::DiffblueCover do
let_it_be(:project) { build(:project) }
subject(:integration) { build(:diffblue_cover_integration, project: project) }
describe 'Validations' do
context 'when active' do
before do
integration.active = true
end
it { is_expected.to validate_presence_of(:diffblue_license_key) }
it { is_expected.to validate_presence_of(:diffblue_access_token_name) }
it { is_expected.to validate_presence_of(:diffblue_access_token_secret) }
end
context 'when inactive' do
before do
integration.active = false
end
it { is_expected.not_to validate_presence_of(:diffblue_license_key) }
it { is_expected.not_to validate_presence_of(:diffblue_access_token_name) }
it { is_expected.not_to validate_presence_of(:diffblue_access_token_secret) }
end
end
describe '#avatar_url' do
it 'returns the avatar image path' do
expect(integration.avatar_url).to eq(ActionController::Base.helpers.image_path(
'illustrations/third-party-logos/integrations-logos/diffblue.svg'
))
end
end
describe '#ci-vars' do
let(:ci_vars) do
[
{ key: 'DIFFBLUE_LICENSE_KEY', value: '1234-ABCD-DCBA-4321', public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN_NAME', value: 'Diffblue CI', public: false, masked: true },
{ key: 'DIFFBLUE_ACCESS_TOKEN',
value: 'glpat-00112233445566778899', public: false, masked: true } # gitleaks:allow
]
end
context 'when active' do
before do
integration.active = true
end
it 'returns the required pipeline vars' do
expect(integration.ci_variables).to match_array(ci_vars)
end
end
context 'when inactive' do
before do
integration.active = false
end
it 'does not return the required pipeline vars' do
expect(integration.ci_variables).to be_empty
end
end
end
describe '#diffblue_link' do
it { expect(described_class.diffblue_link).to include("https://www.diffblue.com/try-cover/gitlab/") }
end
end

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
RSpec.shared_examples Integrations::Base::Ewm do
describe 'Validations' do
context 'when integration is active' do
before do
subject.active = true
end
it { is_expected.to validate_presence_of(:project_url) }
it { is_expected.to validate_presence_of(:issues_url) }
it { is_expected.to validate_presence_of(:new_issue_url) }
it_behaves_like 'issue tracker integration URL attribute', :project_url
it_behaves_like 'issue tracker integration URL attribute', :issues_url
it_behaves_like 'issue tracker integration URL attribute', :new_issue_url
end
context 'when integration is inactive' do
before do
subject.active = false
end
it { is_expected.not_to validate_presence_of(:project_url) }
it { is_expected.not_to validate_presence_of(:issues_url) }
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
end
describe "ReferencePatternValidation" do
it "extracts bug" do
expect(subject.reference_pattern.match("This is bug 123")[:issue]).to eq("bug 123")
end
it "extracts task" do
expect(subject.reference_pattern.match("This is task 123.")[:issue]).to eq("task 123")
end
it "extracts work item" do
expect(subject.reference_pattern.match("This is work item 123 now")[:issue]).to eq("work item 123")
end
it "extracts work item" do
expect(subject.reference_pattern.match("workitem 123 at the beginning")[:issue]).to eq("workitem 123")
end
it "extracts defect" do
expect(subject.reference_pattern.match("This is defect 123 defect")[:issue]).to eq("defect 123")
end
it "extracts rtcwi" do
expect(subject.reference_pattern.match("This is rtcwi 123")[:issue]).to eq("rtcwi 123")
end
end
end

View File

@ -1463,10 +1463,10 @@
resolved "https://registry.yarnpkg.com/vuex/-/vuex-4.0.0.tgz#ac877aa76a9c45368c979471e461b520d38e6cf5"
integrity sha512-56VPujlHscP5q/e7Jlpqc40sja4vOhC4uJD1llBCWolVI8ND4+VzisDVkUMl+z5y0MpIImW6HjhNc+ZvuizgOw==
"@gitlab/web-ide@^0.0.1-dev-20250109231656":
version "0.0.1-dev-20250109231656"
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20250109231656.tgz#dd8afb853ae04dae09e53b37710869975092bea7"
integrity sha512-AO6uo8fKkmlavWfqHgYNgzMz6U+ppl1uK23uiWEy3aU16xL1/MIGda4LVxzmWLVnxz3BGg0xdqA/Ipsd4VcxVw==
"@gitlab/web-ide@^0.0.1-dev-20250110172049":
version "0.0.1-dev-20250110172049"
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20250110172049.tgz#c10d85535f272039613866082d9aa6a9068f63b1"
integrity sha512-3duzCxQGRqSoPnOMG8dtepIxHlE2KvysSVrOGDMijajDpi6N68loFaUSM2E/shy+NtSPKQgltYiG1JXPxZlWaw==
"@gleam-lang/highlight.js-gleam@^1.5.0":
version "1.5.0"