Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-04-30 03:10:38 +00:00
parent 12fc930567
commit 696edc7f23
40 changed files with 163 additions and 114 deletions

View File

@ -12,7 +12,6 @@ Gitlab/FeatureFlagKeyDynamic:
- 'app/workers/concerns/worker_attributes.rb' - 'app/workers/concerns/worker_attributes.rb'
- 'app/workers/loose_foreign_keys/cleanup_worker.rb' - 'app/workers/loose_foreign_keys/cleanup_worker.rb'
- 'ee/app/graphql/resolvers/ai/user_available_features_resolver.rb' - 'ee/app/graphql/resolvers/ai/user_available_features_resolver.rb'
- 'ee/app/graphql/resolvers/ai/user_code_suggestions_contexts_resolver.rb'
- 'ee/app/models/concerns/geo/verifiable_replicator.rb' - 'ee/app/models/concerns/geo/verifiable_replicator.rb'
- 'ee/app/models/members/member_role.rb' - 'ee/app/models/members/member_role.rb'
- 'ee/app/services/search/zoekt/info_service.rb' - 'ee/app/services/search/zoekt/info_service.rb'

View File

@ -4,9 +4,6 @@ Layout/LineBreakAfterFinalMixin:
Exclude: Exclude:
- 'app/graphql/resolvers/integrations/exclusions_resolver.rb' - 'app/graphql/resolvers/integrations/exclusions_resolver.rb'
- 'app/graphql/resolvers/projects/deploy_key_resolver.rb' - 'app/graphql/resolvers/projects/deploy_key_resolver.rb'
- 'app/models/alert_management/metric_image.rb'
- 'app/models/protected_branch/merge_access_level.rb'
- 'app/models/protected_branch/push_access_level.rb'
- 'app/policies/project_member_policy.rb' - 'app/policies/project_member_policy.rb'
- 'app/serializers/cluster_serializer.rb' - 'app/serializers/cluster_serializer.rb'
- 'app/serializers/container_repositories_serializer.rb' - 'app/serializers/container_repositories_serializer.rb'
@ -41,21 +38,7 @@ Layout/LineBreakAfterFinalMixin:
- 'app/workers/stuck_export_jobs_worker.rb' - 'app/workers/stuck_export_jobs_worker.rb'
- 'app/workers/user_status_cleanup/batch_worker.rb' - 'app/workers/user_status_cleanup/batch_worker.rb'
- 'app/workers/users/create_statistics_worker.rb' - 'app/workers/users/create_statistics_worker.rb'
- 'db/migrate/20240213223630_add_duo_features_enabled_cascading_setting.rb'
- 'db/post_migrate/20240419122328_swap_vulnerability_feedback_pipeline_id_convert_to_bigint.rb'
- 'db/post_migrate/20240419131607_swap_packages_build_infos_pipeline_id_convert_to_bigint.rb'
- 'db/post_migrate/20240422080018_swap_merge_trains_pipeline_id_convert_to_bigint.rb'
- 'db/post_migrate/20240424111535_swap_merge_request_metrics_pipeline_id_convert_to_bigint.rb'
- 'db/post_migrate/20240513111937_swap_columns_for_p_ci_builds_trigger_request_and_erased_by.rb'
- 'db/post_migrate/20240514081440_swap_vulnerability_occurrence_pipelines_pipeline_id_convert_to_bigint.rb'
- 'ee/app/controllers/explore/dependencies_controller.rb'
- 'ee/app/finders/security/pipeline_vulnerabilities_finder.rb'
- 'ee/app/graphql/subscriptions/ai_completion_response.rb' - 'ee/app/graphql/subscriptions/ai_completion_response.rb'
- 'ee/app/models/approval_project_rules_protected_branch.rb'
- 'ee/app/models/approval_rules/approval_group_rule.rb'
- 'ee/app/models/approval_rules/approval_group_rules_protected_branch.rb'
- 'ee/app/models/vulnerabilities/scanner.rb'
- 'ee/app/models/vulnerabilities/statistic.rb'
- 'ee/app/services/ee/ip_restrictions/update_service.rb' - 'ee/app/services/ee/ip_restrictions/update_service.rb'
- 'ee/app/services/gitlab_subscriptions/trials/base_apply_trial_service.rb' - 'ee/app/services/gitlab_subscriptions/trials/base_apply_trial_service.rb'
- 'ee/app/services/incident_management/oncall_rotations/edit_service.rb' - 'ee/app/services/incident_management/oncall_rotations/edit_service.rb'
@ -79,7 +62,6 @@ Layout/LineBreakAfterFinalMixin:
- 'ee/lib/api/resource_iteration_events.rb' - 'ee/lib/api/resource_iteration_events.rb'
- 'ee/lib/api/resource_weight_events.rb' - 'ee/lib/api/resource_weight_events.rb'
- 'ee/lib/ee/api/entities/project_push_rule.rb' - 'ee/lib/ee/api/entities/project_push_rule.rb'
- 'ee/lib/ee/gitlab/background_migration/create_compliance_standards_adherence.rb'
- 'ee/lib/gitlab/auth/group_saml/identity_linker.rb' - 'ee/lib/gitlab/auth/group_saml/identity_linker.rb'
- 'ee/lib/gitlab/llm/chain/tools/explain_code/prompts/anthropic.rb' - 'ee/lib/gitlab/llm/chain/tools/explain_code/prompts/anthropic.rb'
- 'ee/lib/gitlab/search/zoekt/client.rb' - 'ee/lib/gitlab/search/zoekt/client.rb'
@ -103,8 +85,6 @@ Layout/LineBreakAfterFinalMixin:
- 'lib/feature.rb' - 'lib/feature.rb'
- 'lib/gitlab/auth/current_user_mode.rb' - 'lib/gitlab/auth/current_user_mode.rb'
- 'lib/gitlab/auth/otp/strategies/forti_token_cloud.rb' - 'lib/gitlab/auth/otp/strategies/forti_token_cloud.rb'
- 'lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex.rb'
- 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
- 'lib/gitlab/ci/config/interpolation/config.rb' - 'lib/gitlab/ci/config/interpolation/config.rb'
- 'lib/gitlab/cluster/rack_timeout_observer.rb' - 'lib/gitlab/cluster/rack_timeout_observer.rb'
- 'lib/gitlab/etag_caching/router/graphql.rb' - 'lib/gitlab/etag_caching/router/graphql.rb'

View File

@ -142,12 +142,12 @@ export default {
}, },
detailsPath() { detailsPath() {
if (this.isLink) { if (this.isLink) {
return this.status.detailsPath; return this.status.detailsPath || this.status.deploymentDetailsPath;
} }
return null; return null;
}, },
hasDetails() { hasDetails() {
return this.status.hasDetails; return this.status.hasDetails || this.status.deploymentDetailsPath;
}, },
hasRetryAction() { hasRetryAction() {
return Boolean(this.job?.status?.action?.title === RETRY_ACTION_TITLE); return Boolean(this.job?.status?.action?.title === RETRY_ACTION_TITLE);
@ -221,7 +221,7 @@ export default {
* @return {Boolean} * @return {Boolean}
*/ */
hasAction() { hasAction() {
return this.job.status && this.job.status.action && this.job.status.action.path; return this.job.status?.action?.path && this.job.status?.action?.icon;
}, },
hasManualConfirmationMessage() { hasManualConfirmationMessage() {
return this.job.status.action.confirmationMessage !== null; return this.job.status.action.confirmationMessage !== null;

View File

@ -325,7 +325,7 @@ export default {
@mouseover="setActionTooltip(true)" @mouseover="setActionTooltip(true)"
@mouseout="setActionTooltip(false)" @mouseout="setActionTooltip(false)"
> >
<gl-icon :name="action.icon" size="12" /> <gl-icon :name="action.icon" :size="12" />
</gl-button> </gl-button>
</div> </div>
</div> </div>

View File

@ -97,6 +97,7 @@
} }
.rd-line-number { .rd-line-number {
position: relative;
padding: 0 10px 0 5px; padding: 0 10px 0 5px;
text-align: right; text-align: right;
background-color: var(--code-line-nubmer-background-color, $white); background-color: var(--code-line-nubmer-background-color, $white);
@ -146,6 +147,12 @@
&::before { &::before {
content: attr(data-line-number); content: attr(data-line-number);
} }
&::after {
content: '';
position: absolute;
inset: 0 -1px;
}
} }
.rd-line-content, .rd-line-content,

View File

@ -412,8 +412,7 @@ class IssuableFinder
strong_memoize(:label_filter) do strong_memoize(:label_filter) do
Issuables::LabelFilter.new( Issuables::LabelFilter.new(
params: original_params, params: original_params,
project: params.project, parent: params.parent
group: params.group
) )
end end
end end
@ -493,15 +492,19 @@ class IssuableFinder
end end
def by_crm_contact(items) def by_crm_contact(items)
return items unless can_filter_by_crm_contact? Issuables::CrmContactFilter.new(
params: original_params,
Issuables::CrmContactFilter.new(params: original_params).filter(items) parent: params.parent,
current_user: current_user
).filter(items)
end end
def by_crm_organization(items) def by_crm_organization(items)
return items unless can_filter_by_crm_organization? Issuables::CrmOrganizationFilter.new(
params: original_params,
Issuables::CrmOrganizationFilter.new(params: original_params).filter(items) parent: params.parent,
current_user: current_user
).filter(items)
end end
def by_subscribed(items) def by_subscribed(items)
@ -516,18 +519,4 @@ class IssuableFinder
items items
end end
end end
def can_filter_by_crm_contact?
current_user&.can?(:read_crm_contact, crm_group)
end
def can_filter_by_crm_organization?
current_user&.can?(:read_crm_organization, crm_group)
end
def crm_group
strong_memoize(:crm_group) do
params.group&.crm_group || params.project&.crm_group
end
end
end end

View File

@ -2,13 +2,13 @@
module Issuables module Issuables
class BaseFilter class BaseFilter
attr_reader :params
FILTER_NONE = 'none' FILTER_NONE = 'none'
FILTER_ANY = 'any' FILTER_ANY = 'any'
def initialize(params:) def initialize(params:, parent: nil, current_user: nil)
@params = params @params = params
@parent = parent
@current_user = current_user
end end
def filter def filter
@ -17,6 +17,8 @@ module Issuables
private private
attr_reader :params, :parent, :current_user
def or_params def or_params
params[:or] params[:or]
end end

View File

@ -9,6 +9,7 @@ module Issuables
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_crm_contact(issuables) def by_crm_contact(issuables)
return issuables if params[:crm_contact_id].blank? return issuables if params[:crm_contact_id].blank?
return issuables unless current_user&.can?(:read_crm_contact, parent&.crm_group)
condition = CustomerRelations::IssueContact condition = CustomerRelations::IssueContact
.where(contact_id: params[:crm_contact_id]) .where(contact_id: params[:crm_contact_id])

View File

@ -9,6 +9,7 @@ module Issuables
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_crm_organization(issuables) def by_crm_organization(issuables)
return issuables if params[:crm_organization_id].blank? return issuables if params[:crm_organization_id].blank?
return issuables unless current_user&.can?(:read_crm_organization, parent&.crm_group)
condition = CustomerRelations::IssueContact condition = CustomerRelations::IssueContact
.joins(:contact) .joins(:contact)

View File

@ -5,13 +5,6 @@ module Issuables
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
extend Gitlab::Cache::RequestCache extend Gitlab::Cache::RequestCache
def initialize(project:, group:, **kwargs)
@project = project
@group = group
super(**kwargs)
end
def filter(issuables) def filter(issuables)
filtered = by_label(issuables) filtered = by_label(issuables)
filtered = by_label_union(filtered) filtered = by_label_union(filtered)
@ -40,8 +33,6 @@ module Issuables
private private
attr_reader :project, :group
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_label(issuables) def by_label(issuables)
return issuables unless label_names_from_params.present? return issuables unless label_names_from_params.present?
@ -195,7 +186,7 @@ module Issuables
def root_namespace def root_namespace
strong_memoize(:root_namespace) do strong_memoize(:root_namespace) do
(@project || @group)&.root_ancestor parent&.root_ancestor
end end
end end
end end

View File

@ -4,9 +4,7 @@ module Issues
class ConfidentialityFilter < Issuables::BaseFilter class ConfidentialityFilter < Issuables::BaseFilter
CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::PLANNER CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::PLANNER
def initialize(current_user:, parent:, assignee_filter:, related_groups: nil, **kwargs) def initialize(assignee_filter:, related_groups: nil, **kwargs)
@current_user = current_user
@parent = parent
@assignee_filter = assignee_filter @assignee_filter = assignee_filter
@related_groups = related_groups @related_groups = related_groups
@ -17,14 +15,14 @@ module Issues
issues = issues.confidential_only if params[:confidential] issues = issues.confidential_only if params[:confidential]
# We do not need to do the confidentiality check if we know that only public issues will be returned # We do not need to do the confidentiality check if we know that only public issues will be returned
return issues.public_only if @current_user.blank? || params[:confidential] == false return issues.public_only if current_user.blank? || params[:confidential] == false
return issues if user_can_see_all_confidential_issues? return issues if user_can_see_all_confidential_issues?
issues.public_only.or( issues.public_only.or(
issues.confidential_only.merge( issues.confidential_only.merge(
issues.authored(@current_user) issues.authored(current_user)
.or(issues.assigned_to(@current_user)) .or(issues.assigned_to(current_user))
.or(access_to_parent_exists(issues)) .or(access_to_parent_exists(issues))
) )
).allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/422045') ).allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/422045')
@ -33,18 +31,18 @@ module Issues
private private
def user_can_see_all_confidential_issues? def user_can_see_all_confidential_issues?
Ability.allowed?(@current_user, :read_all_resources) || Ability.allowed?(current_user, :read_all_resources) ||
Ability.allowed?(@current_user, :read_confidential_issues, @parent) || Ability.allowed?(current_user, :read_confidential_issues, parent) ||
# If already filtering by assignee we can skip confidentiality checks since a user # If already filtering by assignee we can skip confidentiality checks since a user
# can always see confidential issues assigned to them. This is just an # can always see confidential issues assigned to them. This is just an
# optimization since a very common use case of this Finder is to load the # optimization since a very common use case of this Finder is to load the
# count of issues assigned to the user for the header bar. # count of issues assigned to the user for the header bar.
@assignee_filter.includes_user?(@current_user) @assignee_filter.includes_user?(current_user)
end end
def access_to_parent_exists(issues) def access_to_parent_exists(issues)
access_to_project_level_issue_exists = issues.where_exists( access_to_project_level_issue_exists = issues.where_exists(
@current_user.authorizations_for_projects( current_user.authorizations_for_projects(
min_access_level: CONFIDENTIAL_ACCESS_LEVEL, min_access_level: CONFIDENTIAL_ACCESS_LEVEL,
related_project_column: 'issues.project_id' related_project_column: 'issues.project_id'
) )
@ -55,7 +53,7 @@ module Issues
access_to_project_level_issue_exists.project_level.or( access_to_project_level_issue_exists.project_level.or(
issues.group_level.in_namespaces( issues.group_level.in_namespaces(
Group.id_in( Group.id_in(
Group.groups_user_can(@related_groups, @current_user, :read_confidential_issues, same_root: true) Group.groups_user_can(@related_groups, current_user, :read_confidential_issues, same_root: true)
) )
) )
) )

View File

@ -129,6 +129,7 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
tooltip tooltip
hasDetails hasDetails
detailsPath detailsPath
deploymentDetailsPath
group group
label label
text text

View File

@ -11,6 +11,8 @@ module Types
null: true, null: true,
calls_gitaly: true, calls_gitaly: true,
description: 'Action information for the status. This includes method, button title, icon, path, and title.' description: 'Action information for the status. This includes method, button title, icon, path, and title.'
field :deployment_details_path, GraphQL::Types::String, null: true,
description: 'Path of the deployment details for the status.'
field :details_path, GraphQL::Types::String, null: true, field :details_path, GraphQL::Types::String, null: true,
description: 'Path of the details for the status.' description: 'Path of the details for the status.'
field :favicon, GraphQL::Types::String, null: true, field :favicon, GraphQL::Types::String, null: true,

View File

@ -3,6 +3,7 @@
module AlertManagement module AlertManagement
class MetricImage < ApplicationRecord class MetricImage < ApplicationRecord
include MetricImageUploading include MetricImageUploading
self.table_name = 'alert_management_alert_metric_images' self.table_name = 'alert_management_alert_metric_images'
belongs_to :alert, class_name: 'AlertManagement::Alert', foreign_key: 'alert_id', inverse_of: :metric_images belongs_to :alert, class_name: 'AlertManagement::Alert', foreign_key: 'alert_id', inverse_of: :metric_images

View File

@ -2,6 +2,7 @@
class ProtectedBranch::MergeAccessLevel < ApplicationRecord class ProtectedBranch::MergeAccessLevel < ApplicationRecord
include ProtectedBranchAccess include ProtectedBranchAccess
# default value for the access_level column # default value for the access_level column
GITLAB_DEFAULT_ACCESS_LEVEL = Gitlab::Access::MAINTAINER GITLAB_DEFAULT_ACCESS_LEVEL = Gitlab::Access::MAINTAINER
end end

View File

@ -3,6 +3,7 @@
class ProtectedBranch::PushAccessLevel < ApplicationRecord class ProtectedBranch::PushAccessLevel < ApplicationRecord
include ProtectedBranchAccess include ProtectedBranchAccess
include ProtectedRefDeployKeyAccess include ProtectedRefDeployKeyAccess
# default value for the access_level column # default value for the access_level column
GITLAB_DEFAULT_ACCESS_LEVEL = Gitlab::Access::MAINTAINER GITLAB_DEFAULT_ACCESS_LEVEL = Gitlab::Access::MAINTAINER

View File

@ -147,16 +147,8 @@ module Boards
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def labels_filter def labels_filter
Issuables::LabelFilter.new(params: {}, project: project, group: group) Issuables::LabelFilter.new(params: {}, parent: parent)
end end
strong_memoize_attr :labels_filter strong_memoize_attr :labels_filter
def group
parent if parent.is_a?(Group)
end
def project
parent if parent.is_a?(Project)
end
end end
end end

View File

@ -95,6 +95,8 @@
- 2 - 2
- - authorized_projects - - authorized_projects
- 2 - 2
- - authz_ldap_admin_role
- 1
- - auto_devops - - auto_devops
- 2 - 2
- - auto_merge - - auto_merge

View File

@ -2,6 +2,7 @@
class AddDuoFeaturesEnabledCascadingSetting < Gitlab::Database::Migration[2.2] class AddDuoFeaturesEnabledCascadingSetting < Gitlab::Database::Migration[2.2]
include Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings include Gitlab::Database::MigrationHelpers::CascadingNamespaceSettings
enable_lock_retries! enable_lock_retries!
milestone '16.10' milestone '16.10'

View File

@ -2,6 +2,7 @@
class SwapVulnerabilityFeedbackPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2] class SwapVulnerabilityFeedbackPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2]
include Gitlab::Database::MigrationHelpers::Swapping include Gitlab::Database::MigrationHelpers::Swapping
milestone '17.0' milestone '17.0'
disable_ddl_transaction! disable_ddl_transaction!

View File

@ -2,6 +2,7 @@
class SwapPackagesBuildInfosPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2] class SwapPackagesBuildInfosPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2]
include Gitlab::Database::MigrationHelpers::Swapping include Gitlab::Database::MigrationHelpers::Swapping
milestone '17.0' milestone '17.0'
disable_ddl_transaction! disable_ddl_transaction!

View File

@ -2,6 +2,7 @@
class SwapMergeTrainsPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2] class SwapMergeTrainsPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2]
include Gitlab::Database::MigrationHelpers::Swapping include Gitlab::Database::MigrationHelpers::Swapping
milestone '17.0' milestone '17.0'
disable_ddl_transaction! disable_ddl_transaction!

View File

@ -2,6 +2,7 @@
class SwapMergeRequestMetricsPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2] class SwapMergeRequestMetricsPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2]
include Gitlab::Database::MigrationHelpers::Swapping include Gitlab::Database::MigrationHelpers::Swapping
milestone '17.0' milestone '17.0'
disable_ddl_transaction! disable_ddl_transaction!

View File

@ -2,6 +2,7 @@
class SwapColumnsForPCiBuildsTriggerRequestAndErasedBy < Gitlab::Database::Migration[2.2] class SwapColumnsForPCiBuildsTriggerRequestAndErasedBy < Gitlab::Database::Migration[2.2]
include ::Gitlab::Database::MigrationHelpers::Swapping include ::Gitlab::Database::MigrationHelpers::Swapping
milestone '17.1' milestone '17.1'
disable_ddl_transaction! disable_ddl_transaction!

View File

@ -2,6 +2,7 @@
class SwapVulnerabilityOccurrencePipelinesPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2] class SwapVulnerabilityOccurrencePipelinesPipelineIdConvertToBigint < Gitlab::Database::Migration[2.2]
include Gitlab::Database::MigrationHelpers::Swapping include Gitlab::Database::MigrationHelpers::Swapping
milestone '17.1' milestone '17.1'
disable_ddl_transaction! disable_ddl_transaction!

View File

@ -1948,6 +1948,29 @@ Input type: `AddProjectToSecurityDashboardInput`
| <a id="mutationaddprojecttosecuritydashboarderrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | <a id="mutationaddprojecttosecuritydashboarderrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationaddprojecttosecuritydashboardproject"></a>`project` | [`Project`](#project) | Project that was added to the Instance Security Dashboard. | | <a id="mutationaddprojecttosecuritydashboardproject"></a>`project` | [`Project`](#project) | Project that was added to the Instance Security Dashboard. |
### `Mutation.adminRolesLdapSync`
{{< details >}}
**Introduced** in GitLab 18.0.
**Status**: Experiment.
{{< /details >}}
Input type: `AdminRolesLdapSyncInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationadminrolesldapsyncclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationadminrolesldapsyncclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationadminrolesldapsyncerrors"></a>`errors` | [`[String!]`](#string) | Errors encountered during operation. |
| <a id="mutationadminrolesldapsyncsuccess"></a>`success` | [`Boolean`](#boolean) | Whether the sync was successfully enqueued. |
### `Mutation.adminSidekiqQueuesDeleteJobs` ### `Mutation.adminSidekiqQueuesDeleteJobs`
Input type: `AdminSidekiqQueuesDeleteJobsInput` Input type: `AdminSidekiqQueuesDeleteJobsInput`
@ -25892,6 +25915,7 @@ Details of the import status of a project.
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="detailedstatusaction"></a>`action` | [`StatusAction`](#statusaction) | Action information for the status. This includes method, button title, icon, path, and title. | | <a id="detailedstatusaction"></a>`action` | [`StatusAction`](#statusaction) | Action information for the status. This includes method, button title, icon, path, and title. |
| <a id="detailedstatusdeploymentdetailspath"></a>`deploymentDetailsPath` | [`String`](#string) | Path of the deployment details for the status. |
| <a id="detailedstatusdetailspath"></a>`detailsPath` | [`String`](#string) | Path of the details for the status. | | <a id="detailedstatusdetailspath"></a>`detailsPath` | [`String`](#string) | Path of the details for the status. |
| <a id="detailedstatusfavicon"></a>`favicon` | [`String`](#string) | Favicon of the status. | | <a id="detailedstatusfavicon"></a>`favicon` | [`String`](#string) | Favicon of the status. |
| <a id="detailedstatusgroup"></a>`group` {{< icon name="warning-solid" >}} | [`String`](#string) | **Deprecated** in GitLab 16.4. The `group` attribute is deprecated. Use `name` instead. | | <a id="detailedstatusgroup"></a>`group` {{< icon name="warning-solid" >}} | [`String`](#string) | **Deprecated** in GitLab 16.4. The `group` attribute is deprecated. Use `name` instead. |

View File

@ -65,7 +65,7 @@ Test the regex patterns carefully. Tool output formats can change over time, and
| Tool | Language | Command | Regex pattern | | Tool | Language | Command | Regex pattern |
|------------|----------|----------------|---------------| |------------|----------|----------------|---------------|
| pytest-cov | Python | `pytest --cov` | `/TOTAL.*? (100(?:\.0+)?\%\|[1-9]?\d(?:\.\d+)?\%)$/` | | pytest-cov | Python | `pytest --cov` | `/TOTAL.*? (100(?:\.0+)?\%\\|[1-9]?\d(?:\.\d+)?\%)$/` |
| Simplecov | Ruby | `rspec spec` | `/\(\d+.\d+\%\) covered/` | | Simplecov | Ruby | `rspec spec` | `/\(\d+.\d+\%\) covered/` |
{{< /tab >}} {{< /tab >}}
@ -84,7 +84,7 @@ Test the regex patterns carefully. Tool output formats can change over time, and
| Tool | Language | Command | Regex pattern | | Tool | Language | Command | Regex pattern |
|-----------|-------------|------------------------------------|---------------| |-----------|-------------|------------------------------------|---------------|
| JaCoCo | Java/Kotlin | `./gradlew test jacocoTestReport` | `/Total.*?([0-9]{1,3})%/` | | JaCoCo | Java/Kotlin | `./gradlew test jacocoTestReport` | `/Total.*?([0-9]{1,3})%/` |
| Scoverage | Scala | `sbt coverage test coverageReport` | `/(?i)total.*? (100(?:\.0+)?\%\|[1-9]?\d(?:\.\d+)?\%)$/` | | Scoverage | Scala | `sbt coverage test coverageReport` | `/(?i)total.*? (100(?:\.0+)?\%\\|[1-9]?\d(?:\.\d+)?\%)$/` |
{{< /tab >}} {{< /tab >}}
@ -131,7 +131,7 @@ Test the regex patterns carefully. Tool output formats can change over time, and
| Tool | Command | Regex pattern | | Tool | Command | Regex pattern |
|-------------|--------------------|---------------| |-------------|--------------------|---------------|
| excoveralls | None | `/\[TOTAL\]\s+(\d+\.\d+)%/` | | excoveralls | None | `/\[TOTAL\]\s+(\d+\.\d+)%/` |
| mix | `mix test --cover` | `/\d+.\d+\%\s+\|\s+Total/` | | mix | `mix test --cover` | `/\d+.\d+\%\s+\\|\s+Total/` |
{{< /tab >}} {{< /tab >}}

View File

@ -70,8 +70,7 @@ module Gitlab
LabelFilter.new( LabelFilter.new(
stage: stage, stage: stage,
params: params, params: params,
project: nil, parent: root_ancestor
group: root_ancestor
).filter(query) ).filter(query)
end end

View File

@ -8,10 +8,10 @@ module Gitlab
class LabelFilter < Issuables::LabelFilter class LabelFilter < Issuables::LabelFilter
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
def initialize(stage:, project:, group:, **kwargs) def initialize(stage:, **kwargs)
@stage = stage @stage = stage
super(project: project, group: group, **kwargs) super(**kwargs)
end end
private private

View File

@ -9,6 +9,7 @@ module Gitlab
# Migration only version of MergeRequest table # Migration only version of MergeRequest table
class MergeRequest < ::ApplicationRecord class MergeRequest < ::ApplicationRecord
include EachBatch include EachBatch
validates :suggested_reviewers, json_schema: { filename: 'merge_request_suggested_reviewers' } validates :suggested_reviewers, json_schema: { filename: 'merge_request_suggested_reviewers' }
CORRECTED_REGEXP_STR = "^(\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP)" CORRECTED_REGEXP_STR = "^(\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP)"

View File

@ -10,6 +10,7 @@ module Gitlab
self.inheritance_column = :_type_disabled self.inheritance_column = :_type_disabled
include ::Integrations::BaseDataFields include ::Integrations::BaseDataFields
attr_encrypted :url, encryption_options attr_encrypted :url, encryption_options
attr_encrypted :api_url, encryption_options attr_encrypted :api_url, encryption_options

View File

@ -91,6 +91,10 @@ module Gitlab
def confirmation_message def confirmation_message
nil nil
end end
def deployment_details_path
nil
end
end end
end end
end end

View File

@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Issuables::CrmContactFilter do RSpec.describe Issuables::CrmContactFilter do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) } let_it_be(:project) { create(:project, group: group) }
let_it_be(:developer) { create(:user, developer_of: group) }
let_it_be(:contact1) { create(:contact, group: group) } let_it_be(:contact1) { create(:contact, group: group) }
let_it_be(:contact2) { create(:contact, group: group) } let_it_be(:contact2) { create(:contact, group: group) }
@ -14,32 +15,36 @@ RSpec.describe Issuables::CrmContactFilter do
let_it_be(:contact2_issue1) { create(:issue, project: project) } let_it_be(:contact2_issue1) { create(:issue, project: project) }
let_it_be(:issues) { Issue.where(id: [contact1_issue1.id, contact1_issue2.id, contact2_issue1.id]) } let_it_be(:issues) { Issue.where(id: [contact1_issue1.id, contact1_issue2.id, contact2_issue1.id]) }
let(:params) { {} }
before_all do before_all do
create(:issue_customer_relations_contact, issue: contact1_issue1, contact: contact1) create(:issue_customer_relations_contact, issue: contact1_issue1, contact: contact1)
create(:issue_customer_relations_contact, issue: contact1_issue2, contact: contact1) create(:issue_customer_relations_contact, issue: contact1_issue2, contact: contact1)
create(:issue_customer_relations_contact, issue: contact2_issue1, contact: contact2) create(:issue_customer_relations_contact, issue: contact2_issue1, contact: contact2)
end end
subject(:crm_contact_filter) { described_class.new(params: params, parent: group, current_user: developer) }
describe 'when a contact has issues' do describe 'when a contact has issues' do
it 'returns all contact1 issues' do it 'returns all contact1 issues' do
params = { crm_contact_id: contact1.id } params[:crm_contact_id] = contact1.id
expect(described_class.new(params: params).filter(issues)).to contain_exactly(contact1_issue1, contact1_issue2) expect(crm_contact_filter.filter(issues)).to contain_exactly(contact1_issue1, contact1_issue2)
end end
it 'returns all contact2 issues' do it 'returns all contact2 issues' do
params = { crm_contact_id: contact2.id } params[:crm_contact_id] = contact2.id
expect(described_class.new(params: params).filter(issues)).to contain_exactly(contact2_issue1) expect(crm_contact_filter.filter(issues)).to contain_exactly(contact2_issue1)
end end
end end
describe 'when a contact has no issues' do describe 'when a contact has no issues' do
it 'returns no issues' do it 'returns no issues' do
contact3 = create(:contact, group: group) contact3 = create(:contact, group: group)
params = { crm_contact_id: contact3.id } params[:crm_contact_id] = contact3.id
expect(described_class.new(params: params).filter(issues)).to be_empty expect(crm_contact_filter.filter(issues)).to be_empty
end end
end end
end end

View File

@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Issuables::CrmOrganizationFilter do RSpec.describe Issuables::CrmOrganizationFilter do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) } let_it_be(:project) { create(:project, group: group) }
let_it_be(:developer) { create(:user, developer_of: group) }
let_it_be(:crm_organization1) { create(:crm_organization, group: group) } let_it_be(:crm_organization1) { create(:crm_organization, group: group) }
let_it_be(:crm_organization2) { create(:crm_organization, group: group) } let_it_be(:crm_organization2) { create(:crm_organization, group: group) }
@ -17,32 +18,36 @@ RSpec.describe Issuables::CrmOrganizationFilter do
let_it_be(:contact3_issue) { create(:issue, project: project) } let_it_be(:contact3_issue) { create(:issue, project: project) }
let_it_be(:issues) { Issue.where(id: [contact1_issue.id, contact2_issue.id, contact3_issue.id]) } let_it_be(:issues) { Issue.where(id: [contact1_issue.id, contact2_issue.id, contact3_issue.id]) }
let(:params) { {} }
before_all do before_all do
create(:issue_customer_relations_contact, issue: contact1_issue, contact: contact1) create(:issue_customer_relations_contact, issue: contact1_issue, contact: contact1)
create(:issue_customer_relations_contact, issue: contact2_issue, contact: contact2) create(:issue_customer_relations_contact, issue: contact2_issue, contact: contact2)
create(:issue_customer_relations_contact, issue: contact3_issue, contact: contact3) create(:issue_customer_relations_contact, issue: contact3_issue, contact: contact3)
end end
subject(:crm_organization_filter) { described_class.new(params: params, parent: group, current_user: developer) }
describe 'when an organization has issues' do describe 'when an organization has issues' do
it 'returns all crm_organization1 issues' do it 'returns all crm_organization1 issues' do
params = { crm_organization_id: crm_organization1.id } params[:crm_organization_id] = crm_organization1.id
expect(described_class.new(params: params).filter(issues)).to contain_exactly(contact1_issue, contact2_issue) expect(crm_organization_filter.filter(issues)).to contain_exactly(contact1_issue, contact2_issue)
end end
it 'returns all crm_organization2 issues' do it 'returns all crm_organization2 issues' do
params = { crm_organization_id: crm_organization2.id } params[:crm_organization_id] = crm_organization2.id
expect(described_class.new(params: params).filter(issues)).to contain_exactly(contact3_issue) expect(crm_organization_filter.filter(issues)).to contain_exactly(contact3_issue)
end end
end end
describe 'when an organization has no issues' do describe 'when an organization has no issues' do
it 'returns no issues' do it 'returns no issues' do
crm_organization3 = create(:crm_organization, group: group) crm_organization3 = create(:crm_organization, group: group)
params = { crm_organization_id: crm_organization3.id } params[:crm_organization_id] = crm_organization3.id
expect(described_class.new(params: params).filter(issues)).to be_empty expect(crm_organization_filter.filter(issues)).to be_empty
end end
end end
end end

View File

@ -11,6 +11,7 @@ import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_help
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
import { import {
delayedJob, delayedJob,
bridgeJob,
mockJob, mockJob,
mockJobWithoutDetails, mockJobWithoutDetails,
mockJobWithUnauthorizedAction, mockJobWithUnauthorizedAction,
@ -84,6 +85,12 @@ describe('pipeline graph job item', () => {
expect(wrapper.text()).toBe(mockJob.name); expect(wrapper.text()).toBe(mockJob.name);
}); });
it('should use deployment details path as the details path for manual bridge jobs', () => {
createWrapper({ props: { job: bridgeJob } });
expect(findJobItemContent().attributes('href')).toBe(bridgeJob.status.deploymentDetailsPath);
});
}); });
describe('name without link', () => { describe('name without link', () => {

View File

@ -222,6 +222,7 @@ export const wrappedPipelineReturn = {
label: 'passed', label: 'passed',
hasDetails: true, hasDetails: true,
detailsPath: '/root/elemenohpee/-/jobs/1662', detailsPath: '/root/elemenohpee/-/jobs/1662',
deploymentDetailsPath: null,
group: 'success', group: 'success',
action: { action: {
__typename: 'StatusAction', __typename: 'StatusAction',
@ -300,6 +301,26 @@ export const delayedJob = {
}, },
}; };
export const bridgeJob = {
id: 4255,
name: 'test',
kind: BRIDGE_KIND,
status: {
icon: 'status_manual',
text: 'Manual',
label: 'manual action',
tooltip: 'View deployment details page',
group: 'manual',
deploymentDetailsPath: 'path/to/deployment',
hasDetails: false,
action: {
icon: null,
title: null,
path: null,
},
},
};
export const mockJob = { export const mockJob = {
id: 4256, id: 4256,
name: 'test', name: 'test',

View File

@ -1215,6 +1215,7 @@ export const mockPipelineStatusUpdatedResponse = {
__typename: 'Pipeline', __typename: 'Pipeline',
detailedStatus: { detailedStatus: {
detailsPath: '/root/simple-ci-project/-/pipelines/1257', detailsPath: '/root/simple-ci-project/-/pipelines/1257',
deploymentDetailsPath: null,
icon: 'status_success', icon: 'status_success',
id: 'success-1255-1255', id: 'success-1255-1255',
label: 'passed', label: 'passed',

View File

@ -14,7 +14,7 @@ RSpec.describe Types::Ci::DetailedStatusType do
:id, :group, :icon, :favicon, :id, :group, :icon, :favicon,
:details_path, :has_details, :details_path, :has_details,
:label, :name, :text, :tooltip, :label, :name, :text, :tooltip,
:action :action, :deployment_details_path
) )
end end

View File

@ -20,4 +20,10 @@ RSpec.describe Gitlab::Ci::Status::Core do
expect(status.confirmation_message).to be_nil expect(status.confirmation_message).to be_nil
end end
end end
describe '#deployment_details_path' do
it 'returns nil by default' do
expect(status.deployment_details_path).to be_nil
end
end
end end

View File

@ -18,20 +18,20 @@ RSpec.describe 'cross-database foreign keys' do
'system_access_microsoft_applications.namespace_id', 'system_access_microsoft_applications.namespace_id',
'ci_runner_taggings.tag_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/467664 'ci_runner_taggings.tag_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/467664
'ci_runner_taggings_instance_type.tag_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/467664 'ci_runner_taggings_instance_type.tag_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/467664
'ci_secure_file_states.ci_secure_file_id', 'ci_secure_file_states.ci_secure_file_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'dependency_proxy_blob_states.dependency_proxy_blob_id', 'dependency_proxy_blob_states.dependency_proxy_blob_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'dependency_proxy_blob_states.group_id', 'dependency_proxy_blob_states.group_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'dependency_proxy_manifest_states.dependency_proxy_manifest_id', 'dependency_proxy_manifest_states.dependency_proxy_manifest_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'dependency_proxy_manifest_states.group_id', 'dependency_proxy_manifest_states.group_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'lfs_objects_projects.lfs_object_id', 'lfs_objects_projects.lfs_object_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'merge_request_diff_details.merge_request_diff_id', 'merge_request_diff_details.merge_request_diff_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'merge_request_diff_details.project_id', 'merge_request_diff_details.project_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'pages_deployment_states.pages_deployment_id', 'pages_deployment_states.pages_deployment_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'pages_deployment_states.project_id', 'pages_deployment_states.project_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'snippet_repositories.snippet_id', 'snippet_repositories.snippet_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'snippet_repositories.snippet_organization_id', 'snippet_repositories.snippet_organization_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'snippet_repositories.snippet_project_id', 'snippet_repositories.snippet_project_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'upload_states.upload_id', 'upload_states.upload_id', # https://gitlab.com/groups/gitlab-org/-/epics/17347
'application_settings.web_ide_oauth_application_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/531355 'application_settings.web_ide_oauth_application_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/531355
'ai_settings.amazon_q_oauth_application_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/531356 'ai_settings.amazon_q_oauth_application_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/531356
'ai_settings.duo_workflow_oauth_application_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/531356 'ai_settings.duo_workflow_oauth_application_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/531356