Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-05-21 12:11:45 +00:00
parent ad393550bb
commit a013498719
71 changed files with 1160 additions and 272 deletions

View File

@ -343,8 +343,8 @@
{"name":"ipaddress","version":"0.8.3","platform":"ruby","checksum":"85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5"},
{"name":"irb","version":"1.15.1","platform":"ruby","checksum":"d9bca745ac4207a8b728a52b98b766ca909b86ff1a504bcde3d6f8c84faae890"},
{"name":"jaeger-client","version":"1.1.0","platform":"ruby","checksum":"cb5e9b9bbee6ee8d6a82d03d947a5b04543d8c0a949c22e484254f18d8a458a8"},
{"name":"jaro_winkler","version":"1.6.0","platform":"java","checksum":"6cbb36eb4c2649834124d8b92957e577890e8157dd41be8252fde5b02b63b42b"},
{"name":"jaro_winkler","version":"1.6.0","platform":"ruby","checksum":"8b081ab4ba7da5d16b438e62c4be58b87724bfeeb1527e62603f05ab0a2cc424"},
{"name":"jaro_winkler","version":"1.6.1","platform":"java","checksum":"e4f64bc73edbd6210861be99691d890cddb34d77b97d0615995c06bc26ee6cdb"},
{"name":"jaro_winkler","version":"1.6.1","platform":"ruby","checksum":"c056b61bbf7f1fc0151bde7c8f589a2d666d42d0cdb889395b9b73b328e1b393"},
{"name":"jira-ruby","version":"2.3.0","platform":"ruby","checksum":"abf26e6bff4a8ea40bae06f7df6276a5776905c63fb2070934823ca54f62eb62"},
{"name":"jmespath","version":"1.6.2","platform":"ruby","checksum":"238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1"},
{"name":"js_regex","version":"3.8.0","platform":"ruby","checksum":"7934bcdd5a0e6d5af4a520288fd4684a02a472ae55831d9178ccaf82356344b5"},
@ -713,7 +713,7 @@
{"name":"slack-messenger","version":"2.3.6","platform":"ruby","checksum":"58581e587debcbb769336cc7ebe4eb6ae411947fccf347e967a17ac9813e66d8"},
{"name":"snaky_hash","version":"2.0.0","platform":"ruby","checksum":"fe8b2e39e8ff69320f7812af73ea06401579e29ff1734a7009567391600687de"},
{"name":"snowplow-tracker","version":"0.8.0","platform":"ruby","checksum":"7ba6f4f1443a829845fd28e63eda72d9d3d247f485310ddcccaebbc52b734a38"},
{"name":"solargraph","version":"0.54.2","platform":"ruby","checksum":"fe22f56ec2efe64f674b0e9dd3ac8a99df5b5833c2ca84993bdb2af2bb0b6c56"},
{"name":"solargraph","version":"0.54.4","platform":"ruby","checksum":"842705cc511a085e967314de8bd2dd89b00f90238b5582e665ffa39efbd880e0"},
{"name":"solargraph-rspec","version":"0.5.1","platform":"ruby","checksum":"0dfc9124f17b23e95c30acb82c1f799c865408a56b17099b2d6d7b23a76bface"},
{"name":"sorbet-runtime","version":"0.5.11647","platform":"ruby","checksum":"64b65112f2e6a5323310ca9ac0d7d9a6be63aade5a62a6225fe066042ff4fdb6"},
{"name":"spamcheck","version":"1.3.3","platform":"ruby","checksum":"3a29ba9dfcd59543d88054d38c657f79e0a6cf44d763df08ad47680abed50ec7"},

View File

@ -1058,7 +1058,7 @@ GEM
jaeger-client (1.1.0)
opentracing (~> 0.3)
thrift
jaro_winkler (1.6.0)
jaro_winkler (1.6.1)
jira-ruby (2.3.0)
activesupport
atlassian-jwt
@ -1831,12 +1831,12 @@ GEM
hashie
version_gem (~> 1.1)
snowplow-tracker (0.8.0)
solargraph (0.54.2)
solargraph (0.54.4)
backport (~> 1.2)
benchmark (~> 0.4)
bundler (~> 2.0)
diff-lcs (~> 1.4)
jaro_winkler (~> 1.6)
jaro_winkler (~> 1.6, >= 1.6.1)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.1)
logger (~> 1.6)

View File

@ -343,8 +343,8 @@
{"name":"ipaddress","version":"0.8.3","platform":"ruby","checksum":"85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5"},
{"name":"irb","version":"1.15.1","platform":"ruby","checksum":"d9bca745ac4207a8b728a52b98b766ca909b86ff1a504bcde3d6f8c84faae890"},
{"name":"jaeger-client","version":"1.1.0","platform":"ruby","checksum":"cb5e9b9bbee6ee8d6a82d03d947a5b04543d8c0a949c22e484254f18d8a458a8"},
{"name":"jaro_winkler","version":"1.6.0","platform":"java","checksum":"6cbb36eb4c2649834124d8b92957e577890e8157dd41be8252fde5b02b63b42b"},
{"name":"jaro_winkler","version":"1.6.0","platform":"ruby","checksum":"8b081ab4ba7da5d16b438e62c4be58b87724bfeeb1527e62603f05ab0a2cc424"},
{"name":"jaro_winkler","version":"1.6.1","platform":"java","checksum":"e4f64bc73edbd6210861be99691d890cddb34d77b97d0615995c06bc26ee6cdb"},
{"name":"jaro_winkler","version":"1.6.1","platform":"ruby","checksum":"c056b61bbf7f1fc0151bde7c8f589a2d666d42d0cdb889395b9b73b328e1b393"},
{"name":"jira-ruby","version":"2.3.0","platform":"ruby","checksum":"abf26e6bff4a8ea40bae06f7df6276a5776905c63fb2070934823ca54f62eb62"},
{"name":"jmespath","version":"1.6.2","platform":"ruby","checksum":"238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1"},
{"name":"js_regex","version":"3.8.0","platform":"ruby","checksum":"7934bcdd5a0e6d5af4a520288fd4684a02a472ae55831d9178ccaf82356344b5"},
@ -713,7 +713,7 @@
{"name":"slack-messenger","version":"2.3.6","platform":"ruby","checksum":"58581e587debcbb769336cc7ebe4eb6ae411947fccf347e967a17ac9813e66d8"},
{"name":"snaky_hash","version":"2.0.0","platform":"ruby","checksum":"fe8b2e39e8ff69320f7812af73ea06401579e29ff1734a7009567391600687de"},
{"name":"snowplow-tracker","version":"0.8.0","platform":"ruby","checksum":"7ba6f4f1443a829845fd28e63eda72d9d3d247f485310ddcccaebbc52b734a38"},
{"name":"solargraph","version":"0.54.2","platform":"ruby","checksum":"fe22f56ec2efe64f674b0e9dd3ac8a99df5b5833c2ca84993bdb2af2bb0b6c56"},
{"name":"solargraph","version":"0.54.4","platform":"ruby","checksum":"842705cc511a085e967314de8bd2dd89b00f90238b5582e665ffa39efbd880e0"},
{"name":"solargraph-rspec","version":"0.5.1","platform":"ruby","checksum":"0dfc9124f17b23e95c30acb82c1f799c865408a56b17099b2d6d7b23a76bface"},
{"name":"sorbet-runtime","version":"0.5.11647","platform":"ruby","checksum":"64b65112f2e6a5323310ca9ac0d7d9a6be63aade5a62a6225fe066042ff4fdb6"},
{"name":"spamcheck","version":"1.3.3","platform":"ruby","checksum":"3a29ba9dfcd59543d88054d38c657f79e0a6cf44d763df08ad47680abed50ec7"},

View File

@ -1058,7 +1058,7 @@ GEM
jaeger-client (1.1.0)
opentracing (~> 0.3)
thrift
jaro_winkler (1.6.0)
jaro_winkler (1.6.1)
jira-ruby (2.3.0)
activesupport
atlassian-jwt
@ -1831,12 +1831,12 @@ GEM
hashie
version_gem (~> 1.1)
snowplow-tracker (0.8.0)
solargraph (0.54.2)
solargraph (0.54.4)
backport (~> 1.2)
benchmark (~> 0.4)
bundler (~> 2.0)
diff-lcs (~> 1.4)
jaro_winkler (~> 1.6)
jaro_winkler (~> 1.6, >= 1.6.1)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.1)
logger (~> 1.6)

View File

@ -28,6 +28,7 @@ export default (selector, editing = false) => {
settingsLink,
canSetPipelineVariables,
timezoneData,
workerCronExpression,
} = containerEl.dataset;
return new Vue({
@ -43,6 +44,7 @@ export default (selector, editing = false) => {
projectPath,
schedulesPath,
settingsLink,
workerCronExpression,
},
render(createElement) {
return createElement(PipelineSchedulesForm, {

View File

@ -288,6 +288,11 @@
"MergeRequestReviewer",
"UserCore"
],
"UserLevelPermissions": [
"GroupNamespaceUserLevelPermissions",
"ProjectNamespaceUserLevelPermissions",
"UserNamespaceUserLevelPermissions"
],
"VulnerabilityDetail": [
"VulnerabilityDetailBase",
"VulnerabilityDetailBoolean",

View File

@ -39,7 +39,7 @@ const formatMessage = (findings, contentType) => {
const header = sprintf(i18n.promptMessage(findings.length), { contentType });
const matchedPatterns = findings.map(({ patternName, matchedString }) => {
return `<li>${escape(patternName)}: ${escape(matchedString)}</li>`;
return `<li class='gl-wrap-anywhere'>${escape(patternName)}: ${escape(matchedString)}</li>`;
});
const message = `

View File

@ -26,6 +26,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
inject: ['workerCronExpression'],
props: {
initialCronInterval: {
type: String,
@ -54,6 +55,7 @@ export default {
radioValue: this.initialCronInterval ? KEY_CUSTOM : KEY_EVERY_DAY,
cronInterval: this.initialCronInterval,
cronSyntaxUrl: `${DOCS_URL_IN_EE_DIR}/topics/cron/`,
pipelineScheduleWorkerUrl: `${DOCS_URL_IN_EE_DIR}/administration/cicd/#change-maximum-scheduled-pipeline-frequency`,
};
},
computed: {
@ -149,12 +151,22 @@ export default {
i18n: {
learnCronSyntax: s__('PipelineScheduleIntervalPattern|Set a custom interval with Cron syntax.'),
cronSyntaxLink: s__('PipelineScheduleIntervalPattern|What is Cron syntax?'),
pipelineScheduleWorkerExplanation: s__(
'PipelineScheduleIntervalPattern|Pipelines cannot run more frequently than the pipeline schedule worker cron setting (%{workerCronExpression}) allows.',
),
pipelineScheduleWorkerLink: s__('PipelineScheduleIntervalPattern|Learn more.'),
},
};
</script>
<template>
<div>
<p class="gl-mb-3 gl-mt-0 gl-text-subtle" data-testid="worker-cron-expression-hint">
{{ sprintf($options.i18n.pipelineScheduleWorkerExplanation, { workerCronExpression }) }}
<gl-link :href="pipelineScheduleWorkerUrl" target="_blank">
{{ $options.i18n.pipelineScheduleWorkerLink }}
</gl-link>
</p>
<gl-form-radio-group v-model="radioValue" :name="inputNameAttribute">
<gl-form-radio
v-for="option in radioOptions"

View File

@ -1,6 +1,8 @@
import PersistentUserCallout from './persistent_user_callout';
const PERSISTENT_USER_CALLOUTS = [
'.js-persistent-callout',
// NOTE: unique callouts selectors are to be removed within https://gitlab.com/gitlab-org/gitlab/-/issues/527723
'.js-recovery-settings-callout',
'.js-users-over-license-callout',
'.js-admin-licensed-user-count-threshold',
@ -32,7 +34,6 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-pipl-compliance-alert',
'.gcp-signup-offer',
'.js-gold-trial-callout',
'.js-namespace-user-cap-reached-alert',
'.js-data-collection-callout',
];

View File

@ -67,6 +67,8 @@ export default {
:variant="iconVariant"
:class="iconClass"
/>
<span v-if="workItemTypeText" :class="{ 'gl-sr-only': !showText }">{{ workItemTypeText }}</span>
<span v-if="workItemTypeText" :class="{ 'gl-sr-only !gl-absolute': !showText }">{{
workItemTypeText
}}</span>
</span>
</template>

View File

@ -137,6 +137,8 @@ $container-margin: $gl-padding;
$container-margin-xl: $gl-padding-24;
$gl-border-radius-base: 4px;
$gl-border-radius-base-inner: $gl-border-radius-base - 1px;
$gl-border-radius-lg: 8px;
$gl-border-radius-lg-inner: $gl-border-radius-lg - 1px;
$border-radius-small: 2px;
$border-radius-large: 8px;
$default-icon-size: 16px;

View File

@ -457,7 +457,7 @@ $diff-file-header-top: 11px;
}
.mr-section-container {
@apply gl-bg-section gl-border gl-border-section gl-rounded-base;
@apply gl-bg-section gl-border gl-border-section gl-rounded-lg;
&:not(:first-child) {
@apply gl-mt-5;
@ -475,14 +475,14 @@ $diff-file-header-top: 11px;
> .mr-widget-section {
&:first-child {
border-top-left-radius: $gl-border-radius-base-inner;
border-top-right-radius: $gl-border-radius-base-inner;
border-top-left-radius: $gl-border-radius-lg-inner;
border-top-right-radius: $gl-border-radius-lg-inner;
}
> :last-child,
.deploy-heading:last-child {
border-bottom-left-radius: $gl-border-radius-base-inner;
border-bottom-right-radius: $gl-border-radius-base-inner;
border-bottom-left-radius: $gl-border-radius-lg-inner;
border-bottom-right-radius: $gl-border-radius-lg-inner;
}
}

View File

@ -5,7 +5,8 @@ module BizibleCSP
included do
content_security_policy do |policy|
next unless helpers.bizible_enabled? || policy.directives.present?
next unless helpers.bizible_enabled?
next unless policy.directives.present?
default_script_src = policy.directives['script-src'] || policy.directives['default-src']
script_src_values = Array.wrap(default_script_src) | ["'unsafe-eval'", 'https://cdn.bizible.com/scripts/bizible.js']

View File

@ -4,6 +4,12 @@
# with widgets support. Because WorkItems are internally Issues, WorkItemsFinder
# can be almost identical to IssuesFinder, except it should return instances of
# WorkItems instead of Issues
# Arguments:
# klass - actual WorkItems class
# current_user - currently logged in user, if any
# params:
# work_item_parent_ids: integer[] (list of work item ids)
#
module WorkItems
class WorkItemsFinder < IssuesFinder
include Gitlab::Utils::StrongMemoize
@ -25,8 +31,8 @@ module WorkItems
# We require namespace_level_work_items to be true here, since we need the namespace_ids CTE provided by the
# by_parent method for performance reasons see: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181904
items = by_timeframe(items) if include_namespace_level_work_items?
by_widgets(items)
items = by_widgets(items)
by_work_item_parent_ids(items)
end
def by_widgets(items)
@ -47,6 +53,14 @@ module WorkItems
nil
end
def by_work_item_parent_ids(items)
work_item_parent_ids = params[:work_item_parent_ids]
return items unless work_item_parent_ids.present?
items.with_work_item_parent_ids(work_item_parent_ids)
end
override :use_full_text_search?
def use_full_text_search?
return false if include_namespace_level_work_items?

View File

@ -98,10 +98,25 @@ module WorkItems
value.to_h
}
argument :parent_ids, [::Types::GlobalIDType[::WorkItem]],
description: 'Filter work items by global IDs of their parent items (maximum is 100 items).',
required: false,
prepare: ->(global_ids, _ctx) { GitlabSchema.parse_gids(global_ids, expected_type: ::WorkItem).map(&:model_id) }
validates mutually_exclusive: [:assignee_usernames, :assignee_wildcard_id]
validates mutually_exclusive: [:milestone_title, :milestone_wildcard_id]
end
MAX_PARENT_IDS = 100
def resolve(**args)
if args[:parent_ids].to_a.size > MAX_PARENT_IDS
raise GraphQL::ExecutionError, "You can only provide up to #{MAX_PARENT_IDS} parent IDs at once."
end
super
end
private
override :prepare_finder_params
@ -116,6 +131,8 @@ module WorkItems
rewrite_param_name(params[:or], :author_usernames, :author_username)
rewrite_param_name(params[:or], :label_names, :label_name)
rewrite_param_name(params, :parent_ids, :work_item_parent_ids)
params
end

View File

@ -140,6 +140,13 @@ module Types
method: :itself,
experiment: { milestone: '18.1' }
field :user_level_permissions,
Types::Namespaces::UserLevelPermissions,
null: true,
description: 'User permissions on the namespace.',
method: :itself,
experiment: { milestone: '18.1' }
markdown_field :description_html, null: true
def achievements_path

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
module Types
module Namespaces
module UserLevelPermissions
include ::Types::BaseInterface
include ::IssuablesHelper
graphql_name 'UserLevelPermissions'
# rubocop:disable Layout/LineLength -- Expected file length
TYPE_MAPPINGS = {
::Group => ::Types::Namespaces::UserLevelPermissions::GroupNamespaceUserLevelPermissionsType,
::Namespaces::ProjectNamespace => ::Types::Namespaces::UserLevelPermissions::ProjectNamespaceUserLevelPermissionsType,
::Namespaces::UserNamespace => ::Types::Namespaces::UserLevelPermissions::UserNamespaceUserLevelPermissionsType
}.freeze
# rubocop:enable Layout/LineLength
field :can_admin_label,
GraphQL::Types::Boolean,
null: true,
description: 'Whether the current user can admin labels in the namespace.',
fallback_value: false
field :can_create_projects,
GraphQL::Types::Boolean,
null: true,
description: 'Whether the current user can create projects in the namespace.',
fallback_value: false
def self.type_mappings
TYPE_MAPPINGS
end
def self.resolve_type(object, _context)
type_mappings[object.class] || raise("Unknown GraphQL type for namespace type #{object.class}")
end
orphan_types(*type_mappings.values)
end
end
end
::Types::Namespaces::UserLevelPermissions.prepend_mod

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
module Types
module Namespaces
module UserLevelPermissions
class GroupNamespaceUserLevelPermissionsType < BaseObject # rubocop:disable Graphql/AuthorizeTypes -- parent is already authorized
graphql_name 'GroupNamespaceUserLevelPermissions'
implements ::Types::Namespaces::UserLevelPermissions
alias_method :group, :object
def can_admin_label
can?(current_user, :admin_label, group)
end
def can_create_projects
can?(current_user, :create_projects, group)
end
end
end
end
end
::Types::Namespaces::UserLevelPermissions::GroupNamespaceUserLevelPermissionsType.prepend_mod

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
module Types
module Namespaces
module UserLevelPermissions
class ProjectNamespaceUserLevelPermissionsType < BaseObject # rubocop:disable Graphql/AuthorizeTypes -- parent is already authorized
graphql_name 'ProjectNamespaceUserLevelPermissions'
implements ::Types::Namespaces::UserLevelPermissions
alias_method :project, :object
def can_admin_label
can?(current_user, :admin_label, project)
end
end
end
end
end
::Types::Namespaces::UserLevelPermissions::ProjectNamespaceUserLevelPermissionsType.prepend_mod

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
module Types
module Namespaces
module UserLevelPermissions
class UserNamespaceUserLevelPermissionsType < BaseObject # rubocop:disable Graphql/AuthorizeTypes -- parent is already authorized
graphql_name 'UserNamespaceUserLevelPermissions'
implements ::Types::Namespaces::UserLevelPermissions
end
end
end
end
::Types::Namespaces::UserLevelPermissions::UserNamespaceUserLevelPermissionsType.prepend_mod

View File

@ -13,7 +13,8 @@ module Ci
schedules_path: pipeline_schedules_path(project),
settings_link: project_settings_ci_cd_path(project),
timezone_data: timezone_data.to_json,
can_set_pipeline_variables: Ability.allowed?(current_user, :set_pipeline_variables, project).to_s
can_set_pipeline_variables: Ability.allowed?(current_user, :set_pipeline_variables, project).to_s,
worker_cron_expression: schedule.worker_cron_expression
}
end
end

View File

@ -50,6 +50,11 @@ class DeployToken < ApplicationRecord
accepts_nested_attributes_for :project_deploy_tokens
scope :active, -> { where("revoked = false AND expires_at >= NOW()") }
scope :with_encrypted_tokens, ->(tokens) {
return none if tokens.empty?
where(token_encrypted: tokens)
}
def self.gitlab_deploy_token
active.find_by(name: GITLAB_DEPLOY_TOKEN_NAME)

View File

@ -53,6 +53,11 @@ class WorkItem < Issue
.merge(::WorkItems::WidgetDefinition.by_enabled_widget_type(type))
end
scope :with_work_item_parent_ids, ->(parent_ids) {
joins("INNER JOIN work_item_parent_links ON work_item_parent_links.work_item_id = issues.id")
.where(work_item_parent_links: { work_item_parent_id: parent_ids })
}
class << self
def find_by_namespace_and_iid!(namespace, iid)
find_by!(namespace: namespace, iid: iid)

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
ActiveContext.configure do |config|
config.enabled = false
config.indexing_enabled = false
end

View File

@ -90,12 +90,12 @@ Each change log entry includes the following details:
Each configuration change has a status:
| Status | Description |
|---|---|
| Initiated | Configuration change is made in Switchboard, but not yet deployed to the instance. |
| Status | Description |
|-------------|-------------|
| Initiated | Configuration change is made in Switchboard, but not yet deployed to the instance. |
| In progress | Configuration change is actively being deployed to the instance. |
| Complete | Configuration change has been deployed to the instance. |
| Delayed | Initial job to deploy a change has failed and the change has not yet been assigned to a new job. |
| Complete | Configuration change has been deployed to the instance. |
| Delayed | Initial job to deploy a change has failed and the change has not yet been assigned to a new job. |
### View the configuration change log

View File

@ -54,12 +54,12 @@ standards for encryption across all storage services.
The following table summarizes the functional differences between these options:
| Encryption key source | AWS-managed keys | Bring your own key (BYOK) |
|---|---|---|
| **Key generation** | Generated automatically if BYOK not provided. | You create your own AWS KMS keys. |
| **Ownership** | AWS manages on your behalf. | You own and manage your keys. |
| **Access control** | Only AWS services using the keys can decrypt and access them. You don't have direct access. | You control access through IAM policies in your AWS account. |
| **Setup** | No setup required. | Must be set up before onboarding. Cannot be enabled later. |
| Encryption key source | AWS-managed keys | Bring your own key (BYOK) |
|-----------------------|---------------------------------------------------------------------------------------------|---------------------------|
| **Key generation** | Generated automatically if BYOK not provided. | You create your own AWS KMS keys. |
| **Ownership** | AWS manages on your behalf. | You own and manage your keys. |
| **Access control** | Only AWS services using the keys can decrypt and access them. You don't have direct access. | You control access through IAM policies in your AWS account. |
| **Setup** | No setup required. | Must be set up before onboarding. Cannot be enabled later. |
### AWS-managed keys

View File

@ -12862,6 +12862,7 @@ Input type: `WorkItemExportInput`
| <a id="mutationworkitemexportmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values `NONE` and `ANY` are supported. |
| <a id="mutationworkitemexportnot"></a>`not` | [`NegatedWorkItemFilterInput`](#negatedworkitemfilterinput) | Negated work item arguments. |
| <a id="mutationworkitemexportor"></a>`or` | [`UnionedWorkItemFilterInput`](#unionedworkitemfilterinput) | List of arguments with inclusive `OR`. |
| <a id="mutationworkitemexportparentids"></a>`parentIds` | [`[WorkItemID!]`](#workitemid) | Filter work items by global IDs of their parent items (maximum is 100 items). |
| <a id="mutationworkitemexportprojectpath"></a>`projectPath` | [`ID!`](#id) | Full project path. |
| <a id="mutationworkitemexportsearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="mutationworkitemexportselectedfields"></a>`selectedFields` | [`[AvailableExportFields!]`](#availableexportfields) | List of selected fields to be exported. Omit to export all available fields. |
@ -13041,6 +13042,7 @@ Input type: `WorkItemsCsvExportInput`
| <a id="mutationworkitemscsvexportmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values `NONE` and `ANY` are supported. |
| <a id="mutationworkitemscsvexportnot"></a>`not` | [`NegatedWorkItemFilterInput`](#negatedworkitemfilterinput) | Negated work item arguments. |
| <a id="mutationworkitemscsvexportor"></a>`or` | [`UnionedWorkItemFilterInput`](#unionedworkitemfilterinput) | List of arguments with inclusive `OR`. |
| <a id="mutationworkitemscsvexportparentids"></a>`parentIds` | [`[WorkItemID!]`](#workitemid) | Filter work items by global IDs of their parent items (maximum is 100 items). |
| <a id="mutationworkitemscsvexportprojectpath"></a>`projectPath` | [`ID!`](#id) | Full project path. |
| <a id="mutationworkitemscsvexportsearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="mutationworkitemscsvexportselectedfields"></a>`selectedFields` | [`[AvailableExportFields!]`](#availableexportfields) | List of selected fields to be exported. Omit to export all available fields. |
@ -24622,6 +24624,7 @@ Represents a vulnerability. The connection type is countable.
| <a id="countablevulnerabilitydismissedby"></a>`dismissedBy` | [`UserCore`](#usercore) | User that dismissed the vulnerability. |
| <a id="countablevulnerabilityexternalissuelinks"></a>`externalIssueLinks` | [`VulnerabilityExternalIssueLinkConnection!`](#vulnerabilityexternalissuelinkconnection) | List of external issue links related to the vulnerability. (see [Connections](#connections)) |
| <a id="countablevulnerabilityfalsepositive"></a>`falsePositive` | [`Boolean`](#boolean) | Indicates whether the vulnerability is a false positive. |
| <a id="countablevulnerabilityfindingtokenstatus"></a>`findingTokenStatus` | [`VulnerabilityFindingTokenStatus`](#vulnerabilityfindingtokenstatus) | Status of the secret token associated with this vulnerability. Returns `null` if the `validity_checks` feature flag is disabled. |
| <a id="countablevulnerabilityhasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Indicates whether there is a remediation available for the vulnerability. |
| <a id="countablevulnerabilityid"></a>`id` | [`ID!`](#id) | GraphQL ID of the vulnerability. |
| <a id="countablevulnerabilityidentifiers"></a>`identifiers` | [`[VulnerabilityIdentifier!]!`](#vulnerabilityidentifier) | Identifiers of the vulnerability. |
@ -25249,6 +25252,7 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="currentuserworkitemsmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values `NONE` and `ANY` are supported. |
| <a id="currentuserworkitemsnot"></a>`not` | [`NegatedWorkItemFilterInput`](#negatedworkitemfilterinput) | Negated work item arguments. |
| <a id="currentuserworkitemsor"></a>`or` | [`UnionedWorkItemFilterInput`](#unionedworkitemfilterinput) | List of arguments with inclusive `OR`. |
| <a id="currentuserworkitemsparentids"></a>`parentIds` | [`[WorkItemID!]`](#workitemid) | Filter work items by global IDs of their parent items (maximum is 100 items). |
| <a id="currentuserworkitemssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="currentuserworkitemssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. |
| <a id="currentuserworkitemsstate"></a>`state` | [`IssuableState`](#issuablestate) | Current state of the work item. |
@ -27911,6 +27915,7 @@ GPG signature for a signed commit.
| <a id="grouptotalrepositorysizeexcess"></a>`totalRepositorySizeExcess` | [`Float`](#float) | Total excess repository size of all projects in the root namespace in bytes. This only applies to namespaces under Project limit enforcement. |
| <a id="grouptwofactorgraceperiod"></a>`twoFactorGracePeriod` | [`Int`](#int) | Time before two-factor authentication is enforced. |
| <a id="groupupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp of when the group was last updated. |
| <a id="groupuserlevelpermissions"></a>`userLevelPermissions` {{< icon name="warning-solid" >}} | [`UserLevelPermissions`](#userlevelpermissions) | **Introduced** in GitLab 18.1. **Status**: Experiment. User permissions on the namespace. |
| <a id="groupuserpermissions"></a>`userPermissions` | [`GroupPermissions!`](#grouppermissions) | Permissions for the current user on the resource. |
| <a id="groupvaluestreamanalytics"></a>`valueStreamAnalytics` | [`ValueStreamAnalytics`](#valuestreamanalytics) | Information about Value Stream Analytics within the group. |
| <a id="groupvisibility"></a>`visibility` | [`String`](#string) | Visibility of the namespace. |
@ -29574,6 +29579,7 @@ Returns [`WorkItemStateCountsType`](#workitemstatecountstype).
| <a id="groupworkitemstatecountsmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values `NONE` and `ANY` are supported. |
| <a id="groupworkitemstatecountsnot"></a>`not` | [`NegatedWorkItemFilterInput`](#negatedworkitemfilterinput) | Negated work item arguments. |
| <a id="groupworkitemstatecountsor"></a>`or` | [`UnionedWorkItemFilterInput`](#unionedworkitemfilterinput) | List of arguments with inclusive `OR`. |
| <a id="groupworkitemstatecountsparentids"></a>`parentIds` | [`[WorkItemID!]`](#workitemid) | Filter work items by global IDs of their parent items (maximum is 100 items). |
| <a id="groupworkitemstatecountsrequirementlegacywidget"></a>`requirementLegacyWidget` {{< icon name="warning-solid" >}} | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in GitLab 15.9. Use work item IID filter instead. |
| <a id="groupworkitemstatecountssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="groupworkitemstatecountssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. |
@ -29646,6 +29652,7 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="groupworkitemsmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values `NONE` and `ANY` are supported. |
| <a id="groupworkitemsnot"></a>`not` | [`NegatedWorkItemFilterInput`](#negatedworkitemfilterinput) | Negated work item arguments. |
| <a id="groupworkitemsor"></a>`or` | [`UnionedWorkItemFilterInput`](#unionedworkitemfilterinput) | List of arguments with inclusive `OR`. |
| <a id="groupworkitemsparentids"></a>`parentIds` | [`[WorkItemID!]`](#workitemid) | Filter work items by global IDs of their parent items (maximum is 100 items). |
| <a id="groupworkitemsrequirementlegacywidget"></a>`requirementLegacyWidget` {{< icon name="warning-solid" >}} | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in GitLab 15.9. Use work item IID filter instead. |
| <a id="groupworkitemssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="groupworkitemssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. |
@ -29846,6 +29853,17 @@ Limited group data accessible to users without full group read access (e.g. non-
| <a id="groupnamespacelinksreportabuse"></a>`reportAbuse` | [`String`](#string) | Namespace report_abuse. |
| <a id="groupnamespacelinkssignin"></a>`signIn` | [`String`](#string) | Namespace sign_in_path. |
### `GroupNamespaceUserLevelPermissions`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="groupnamespaceuserlevelpermissionscanadminlabel"></a>`canAdminLabel` | [`Boolean`](#boolean) | Whether the current user can admin labels in the namespace. |
| <a id="groupnamespaceuserlevelpermissionscanbulkeditepics"></a>`canBulkEditEpics` | [`Boolean`](#boolean) | Whether the current user can bulk edit epics in the group. |
| <a id="groupnamespaceuserlevelpermissionscancreateepic"></a>`canCreateEpic` | [`Boolean`](#boolean) | Whether the current user can create epics in the group. |
| <a id="groupnamespaceuserlevelpermissionscancreateprojects"></a>`canCreateProjects` | [`Boolean`](#boolean) | Whether the current user can create projects in the namespace. |
### `GroupPermissions`
#### Fields
@ -33428,6 +33446,7 @@ Product analytics events for a specific month and year.
| <a id="namespacetimelogcategories"></a>`timelogCategories` {{< icon name="warning-solid" >}} | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | **Introduced** in GitLab 15.3. **Status**: Experiment. Timelog categories for the namespace. |
| <a id="namespacetotalrepositorysize"></a>`totalRepositorySize` | [`Float`](#float) | Total repository size of all projects in the root namespace in bytes. |
| <a id="namespacetotalrepositorysizeexcess"></a>`totalRepositorySizeExcess` | [`Float`](#float) | Total excess repository size of all projects in the root namespace in bytes. This only applies to namespaces under Project limit enforcement. |
| <a id="namespaceuserlevelpermissions"></a>`userLevelPermissions` {{< icon name="warning-solid" >}} | [`UserLevelPermissions`](#userlevelpermissions) | **Introduced** in GitLab 18.1. **Status**: Experiment. User permissions on the namespace. |
| <a id="namespaceuserpermissions"></a>`userPermissions` | [`NamespacePermissions!`](#namespacepermissions) | Permissions for the current user on the resource. |
| <a id="namespacevisibility"></a>`visibility` | [`String`](#string) | Visibility of the namespace. |
| <a id="namespaceweburl"></a>`webUrl` | [`String`](#string) | URL of the object. |
@ -37655,6 +37674,7 @@ Returns [`WorkItemStateCountsType`](#workitemstatecountstype).
| <a id="projectworkitemstatecountsmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values `NONE` and `ANY` are supported. |
| <a id="projectworkitemstatecountsnot"></a>`not` | [`NegatedWorkItemFilterInput`](#negatedworkitemfilterinput) | Negated work item arguments. |
| <a id="projectworkitemstatecountsor"></a>`or` | [`UnionedWorkItemFilterInput`](#unionedworkitemfilterinput) | List of arguments with inclusive `OR`. |
| <a id="projectworkitemstatecountsparentids"></a>`parentIds` | [`[WorkItemID!]`](#workitemid) | Filter work items by global IDs of their parent items (maximum is 100 items). |
| <a id="projectworkitemstatecountsrequirementlegacywidget"></a>`requirementLegacyWidget` {{< icon name="warning-solid" >}} | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in GitLab 15.9. Use work item IID filter instead. |
| <a id="projectworkitemstatecountssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="projectworkitemstatecountssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. |
@ -37723,6 +37743,7 @@ four standard [pagination arguments](#pagination-arguments):
| <a id="projectworkitemsmyreactionemoji"></a>`myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values `NONE` and `ANY` are supported. |
| <a id="projectworkitemsnot"></a>`not` | [`NegatedWorkItemFilterInput`](#negatedworkitemfilterinput) | Negated work item arguments. |
| <a id="projectworkitemsor"></a>`or` | [`UnionedWorkItemFilterInput`](#unionedworkitemfilterinput) | List of arguments with inclusive `OR`. |
| <a id="projectworkitemsparentids"></a>`parentIds` | [`[WorkItemID!]`](#workitemid) | Filter work items by global IDs of their parent items (maximum is 100 items). |
| <a id="projectworkitemsrequirementlegacywidget"></a>`requirementLegacyWidget` {{< icon name="warning-solid" >}} | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in GitLab 15.9. Use work item IID filter instead. |
| <a id="projectworkitemssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="projectworkitemssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. |
@ -37864,6 +37885,17 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
| <a id="projectnamespacelinksreportabuse"></a>`reportAbuse` | [`String`](#string) | Namespace report_abuse. |
| <a id="projectnamespacelinkssignin"></a>`signIn` | [`String`](#string) | Namespace sign_in_path. |
### `ProjectNamespaceUserLevelPermissions`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectnamespaceuserlevelpermissionscanadminlabel"></a>`canAdminLabel` | [`Boolean`](#boolean) | Whether the current user can admin labels in the namespace. |
| <a id="projectnamespaceuserlevelpermissionscanbulkeditepics"></a>`canBulkEditEpics` | [`Boolean`](#boolean) | Whether the current user can bulk edit epics in the group. |
| <a id="projectnamespaceuserlevelpermissionscancreateepic"></a>`canCreateEpic` | [`Boolean`](#boolean) | Whether the current user can create epics in the group. |
| <a id="projectnamespaceuserlevelpermissionscancreateprojects"></a>`canCreateProjects` | [`Boolean`](#boolean) | Whether the current user can create projects in the namespace. |
### `ProjectPermissions`
#### Fields
@ -40683,6 +40715,17 @@ fields relate to interactions between the two entities.
| <a id="usernamespacelinksreportabuse"></a>`reportAbuse` | [`String`](#string) | Namespace report_abuse. |
| <a id="usernamespacelinkssignin"></a>`signIn` | [`String`](#string) | Namespace sign_in_path. |
### `UserNamespaceUserLevelPermissions`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="usernamespaceuserlevelpermissionscanadminlabel"></a>`canAdminLabel` | [`Boolean`](#boolean) | Whether the current user can admin labels in the namespace. |
| <a id="usernamespaceuserlevelpermissionscanbulkeditepics"></a>`canBulkEditEpics` | [`Boolean`](#boolean) | Whether the current user can bulk edit epics in the group. |
| <a id="usernamespaceuserlevelpermissionscancreateepic"></a>`canCreateEpic` | [`Boolean`](#boolean) | Whether the current user can create epics in the group. |
| <a id="usernamespaceuserlevelpermissionscancreateprojects"></a>`canCreateProjects` | [`Boolean`](#boolean) | Whether the current user can create projects in the namespace. |
### `UserPermissions`
#### Fields
@ -40946,6 +40989,7 @@ Represents a vulnerability.
| <a id="vulnerabilitydismissedby"></a>`dismissedBy` | [`UserCore`](#usercore) | User that dismissed the vulnerability. |
| <a id="vulnerabilityexternalissuelinks"></a>`externalIssueLinks` | [`VulnerabilityExternalIssueLinkConnection!`](#vulnerabilityexternalissuelinkconnection) | List of external issue links related to the vulnerability. (see [Connections](#connections)) |
| <a id="vulnerabilityfalsepositive"></a>`falsePositive` | [`Boolean`](#boolean) | Indicates whether the vulnerability is a false positive. |
| <a id="vulnerabilityfindingtokenstatus"></a>`findingTokenStatus` | [`VulnerabilityFindingTokenStatus`](#vulnerabilityfindingtokenstatus) | Status of the secret token associated with this vulnerability. Returns `null` if the `validity_checks` feature flag is disabled. |
| <a id="vulnerabilityhasremediations"></a>`hasRemediations` | [`Boolean`](#boolean) | Indicates whether there is a remediation available for the vulnerability. |
| <a id="vulnerabilityid"></a>`id` | [`ID!`](#id) | GraphQL ID of the vulnerability. |
| <a id="vulnerabilityidentifiers"></a>`identifiers` | [`[VulnerabilityIdentifier!]!`](#vulnerabilityidentifier) | Identifiers of the vulnerability. |
@ -41335,6 +41379,19 @@ Represents an external issue link of a vulnerability.
| <a id="vulnerabilityexternalissuelinkid"></a>`id` | [`VulnerabilitiesExternalIssueLinkID!`](#vulnerabilitiesexternalissuelinkid) | GraphQL ID of the external issue link. |
| <a id="vulnerabilityexternalissuelinklinktype"></a>`linkType` | [`VulnerabilityExternalIssueLinkType!`](#vulnerabilityexternalissuelinktype) | Type of the external issue link. |
### `VulnerabilityFindingTokenStatus`
Represents the status of a secret token found in a vulnerability.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="vulnerabilityfindingtokenstatuscreatedat"></a>`createdAt` | [`Time!`](#time) | When the token status was created. |
| <a id="vulnerabilityfindingtokenstatusid"></a>`id` | [`ID!`](#id) | ID of the finding token status. |
| <a id="vulnerabilityfindingtokenstatusstatus"></a>`status` | [`VulnerabilityFindingTokenStatusState!`](#vulnerabilityfindingtokenstatusstate) | Status of the token (unknown, active, inactive). |
| <a id="vulnerabilityfindingtokenstatusupdatedat"></a>`updatedAt` | [`Time!`](#time) | When the token status was last updated. |
### `VulnerabilityIdentifier`
Represents a vulnerability identifier.
@ -46458,6 +46515,16 @@ The type of the external issue link related to a vulnerability.
| ----- | ----------- |
| <a id="vulnerabilityexternalissuelinktypecreated"></a>`CREATED` | Created link type. |
### `VulnerabilityFindingTokenStatusState`
Status of a secret token found in a vulnerability.
| Value | Description |
| ----- | ----------- |
| <a id="vulnerabilityfindingtokenstatusstateactive"></a>`ACTIVE` | Token is active and can be exploited. |
| <a id="vulnerabilityfindingtokenstatusstateinactive"></a>`INACTIVE` | Token is inactive and cannot be exploited. |
| <a id="vulnerabilityfindingtokenstatusstateunknown"></a>`UNKNOWN` | Token status is unknown. |
### `VulnerabilityGrade`
The grade of the vulnerable project.
@ -49304,6 +49371,23 @@ four standard [pagination arguments](#pagination-arguments):
| ---- | ---- | ----------- |
| <a id="useruserachievementsincludehidden"></a>`includeHidden` | [`Boolean`](#boolean) | Indicates whether or not achievements hidden from the profile should be included in the result. |
#### `UserLevelPermissions`
Implementations:
- [`GroupNamespaceUserLevelPermissions`](#groupnamespaceuserlevelpermissions)
- [`ProjectNamespaceUserLevelPermissions`](#projectnamespaceuserlevelpermissions)
- [`UserNamespaceUserLevelPermissions`](#usernamespaceuserlevelpermissions)
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="userlevelpermissionscanadminlabel"></a>`canAdminLabel` | [`Boolean`](#boolean) | Whether the current user can admin labels in the namespace. |
| <a id="userlevelpermissionscanbulkeditepics"></a>`canBulkEditEpics` | [`Boolean`](#boolean) | Whether the current user can bulk edit epics in the group. |
| <a id="userlevelpermissionscancreateepic"></a>`canCreateEpic` | [`Boolean`](#boolean) | Whether the current user can create epics in the group. |
| <a id="userlevelpermissionscancreateprojects"></a>`canCreateProjects` | [`Boolean`](#boolean) | Whether the current user can create projects in the namespace. |
#### `VulnerabilityStatisticInterface`
Implementations:

View File

@ -24,14 +24,14 @@ Fields removed in GitLab 17.0.
### GraphQL Fields
| Field name | GraphQL type | Deprecated in | Removal MR | Use instead |
|---|---|---|---|---|
| `architectureName` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `executorName` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `ipAddress` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `platformName` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `revision` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `version` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| Field name | GraphQL type | Deprecated in | Removal MR | Use instead |
|--------------------|--------------|---------------|-------------------------------------------------------------------------|-------------|
| `architectureName` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `executorName` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `ipAddress` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `platformName` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `revision` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
| `version` | `CiRunner` | 16.2 | [!124751](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124751) | Use this field in `manager` object instead. |
## GitLab 16.0
@ -39,21 +39,21 @@ Fields removed in GitLab 16.0.
### GraphQL Fields
| Field name | GraphQL type | Deprecated in | Removal MR | Use instead |
|---|---|---|---|---|
| `name` | `PipelineSecurityReportFinding` | [15.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89571) | [!119055](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119055) | `title` |
| `external` | `ReleaseAssetLink` | 15.9 | [!111750](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111750) | None |
| `confidence` | `PipelineSecurityReportFinding` | 15.4 | [!118617](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118617) | None |
| `PAUSED` | `CiRunnerStatus` | 14.8 | [!118635](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118635) | `CiRunner.paused: true` |
| `ACTIVE` | `CiRunnerStatus` | 14.8 | [!118635](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118635) | `CiRunner.paused: false` |
| Field name | GraphQL type | Deprecated in | Removal MR | Use instead |
|--------------|---------------------------------|---------------------------------------------------------------------|-------------------------------------------------------------------------|-------------|
| `name` | `PipelineSecurityReportFinding` | [15.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89571) | [!119055](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119055) | `title` |
| `external` | `ReleaseAssetLink` | 15.9 | [!111750](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111750) | None |
| `confidence` | `PipelineSecurityReportFinding` | 15.4 | [!118617](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118617) | None |
| `PAUSED` | `CiRunnerStatus` | 14.8 | [!118635](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118635) | `CiRunner.paused: true` |
| `ACTIVE` | `CiRunnerStatus` | 14.8 | [!118635](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118635) | `CiRunner.paused: false` |
### GraphQL Mutations
| Argument name | Mutation | Deprecated in | Use instead |
| -------------------- | -------------------- |---------------------------------------------------------------------|------------------------------------------------|
| - | `vulnerabilityFindingDismiss` | [15.5](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99170) | `vulnerabilityDismiss` or `securityFindingDismiss` |
| - | `apiFuzzingCiConfigurationCreate` | [15.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87241) | `todos` |
| - | `CiCdSettingsUpdate` | [15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/361801) | `ProjectCiCdSettingsUpdate` |
| Argument name | Mutation | Deprecated in | Use instead |
|---------------|-----------------------------------|---------------------------------------------------------------------|-------------|
| - | `vulnerabilityFindingDismiss` | [15.5](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99170) | `vulnerabilityDismiss` or `securityFindingDismiss` |
| - | `apiFuzzingCiConfigurationCreate` | [15.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87241) | `todos` |
| - | `CiCdSettingsUpdate` | [15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/361801) | `ProjectCiCdSettingsUpdate` |
## GitLab 15.0
@ -63,20 +63,20 @@ Fields removed in GitLab 15.0.
[Removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85382) in GitLab 15.0:
| Argument name | Mutation | Deprecated in | Use instead |
| -------------------- | -------------------- | ------------- | -------------------------- |
| - | `clusterAgentTokenDelete`| 14.7 | `clusterAgentTokenRevoke` |
| Argument name | Mutation | Deprecated in | Use instead |
|---------------|---------------------------|---------------|-------------|
| - | `clusterAgentTokenDelete` | 14.7 | `clusterAgentTokenRevoke` |
### GraphQL Fields
[Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/342882) in GitLab 15.0:
| Argument name | Field name | Deprecated in | Use instead |
| -------------------- | --------------------| ------------- | -------------------------- |
| - | `pipelines` | 14.5 | None |
| Argument name | Field name | Deprecated in | Use instead |
|---------------|-------------|---------------|-------------|
| - | `pipelines` | 14.5 | None |
### GraphQL Types
| Field name | GraphQL type | Deprecated in | Use instead |
| ------------------------------------------ | ------------------------ | ------------- | ---------------------------------------------------------------------------------- |
| Field name | GraphQL type | Deprecated in | Use instead |
|--------------------------------------------|--------------------------|---------------|-------------|
| `defaultMergeCommitMessageWithDescription` | `GraphQL::Types::String` | 14.5 | None. Define a [merge commit template](../../user/project/merge_requests/commit_templates.md) in your project and use `defaultMergeCommitMessage`. |

View File

@ -240,7 +240,7 @@ The compaction algorithm:
```plaintext
group1/group2/group3
group1/group2/group4
group1/group2/group6
group1/group5/group6
```
1. If the allowlist is over the 200 entry limit, the algorithm compacts again:

View File

@ -39,7 +39,7 @@ Other pages are generated by using non-standard processes. These pages often use
that are coded across multiple repositories.
| Page | Details | Owner |
|---|---|---|
|------|---------|-------|
| [All feature flags in GitLab](../../../user/feature_flags.md) | [Generated during docs build](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/doc/raketasks.md#generate-the-feature-flag-tables) | [Technical Writing](https://handbook.gitlab.com/handbook/product/ux/technical-writing/) |
| [GitLab Runner feature flags](https://docs.gitlab.com/runner/configuration/feature-flags.html) | [Page source](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/ec6e1797d2173a95c8ac7f726bd62f6f110b7211/docs/configuration/feature-flags.md?plain=1#L39) | [Runner](https://handbook.gitlab.com/handbook/engineering/development/ops/verify/runner/) |
| [GitLab Runner Kubernetes API settings](https://docs.gitlab.com/runner/executors/kubernetes/) | Generated with [mage](https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/.gitlab/ci/qa.gitlab-ci.yml#L133) | [Runner](https://handbook.gitlab.com/handbook/engineering/development/ops/verify/runner/) |

View File

@ -324,17 +324,17 @@ documentation even if the probability of a token being exploited is low.
Use these fake tokens as examples:
| Token type | Token value |
|:----------------------|:-------------------------------------------------------------------|
| Personal access token | `<your_access_token>` |
| Token type | Token value |
|:----------------------|:------------|
| Personal access token | `<your_access_token>` |
| Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` |
| Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` |
| CI/CD variable | `Li8j-mLUVA3eZYjPfd_H` |
| Project runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
| Instance runner token | `6Vk7ZsosqQyfreAxXTZr` |
| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |
| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
| CI/CD variable | `Li8j-mLUVA3eZYjPfd_H` |
| Project runner token | `yrnZW46BrtBFqM7xDzE7dddd` |
| Instance runner token | `6Vk7ZsosqQyfreAxXTZr` |
| Trigger token | `be20d8dcc028677c931e04f3871a9b` |
| Webhook secret token | `6XhDroRcYPM5by_h-HLY` |
| Health check token | `Tu7BgjR9qeZTEyRzGG2P` |
### Contractions
@ -346,12 +346,12 @@ Some contractions, however, should be avoided:
<!-- vale gitlab_base.Possessive = NO -->
| Do not use a contraction | Example | Use instead |
|-------------------------------|--------------------------------------------------|------------------------------------------------------------------|
| With a proper noun and a verb | **Terraform's** a helpful tool. | **Terraform** is a helpful tool. |
| To emphasize a negative | **Don't** install X with Y. | **Do not** install X with Y. |
| In reference documentation | **Don't** set a limit. | **Do not** set a limit. |
| In error messages | Requests to localhost **aren't** allowed. | Requests to localhost **are not** allowed. |
| Do not use a contraction | Example | Use instead |
|-------------------------------|-------------------------------------------|-------------|
| With a proper noun and a verb | **Terraform's** a helpful tool. | **Terraform** is a helpful tool. |
| To emphasize a negative | **Don't** install X with Y. | **Do not** install X with Y. |
| In reference documentation | **Don't** set a limit. | **Do not** set a limit. |
| In error messages | Requests to localhost **aren't** allowed. | Requests to localhost **are not** allowed. |
<!-- vale gitlab_base.Possessive = YES -->
@ -745,6 +745,10 @@ To make tables easier to maintain:
| Setting 3 | `0` | Another short description. |
```
Always align the delimiter (second) row of the table with the header (first) row.
Avoid using shortened delimiter rows like `|-|-|-|` or `|--|--|`.
If a large table does not auto-format well, you should still align the delimiter row with the header row.
### Editor extensions for table formatting
To ensure consistent table formatting across all Markdown files, consider formatting your tables
@ -775,7 +779,8 @@ plugin, but it does not have a **Follow header row length** setting.
### Updates to existing tables
When you add or edit rows in an existing table, the cells in the new rows might be wider.
When you add or edit rows in an existing table, some rows might not be aligned anymore.
You do not need to realign the entire table if only changing a few rows.
If you realign the columns to account for the width, the diff becomes difficult to read,
because the entire table shows as modified.
@ -793,9 +798,9 @@ When creating tables of lists of features (such the features
available to each role on the [Permissions](../../../user/permissions.md#project-members-permissions)
page), use these phrases:
| Option | Markdown | Displayed result |
|--------|--------------------------|------------------------|
| No | `{{</* icon name="dash-circle" */>}} No` | {{< icon name="dash-circle" >}} No |
| Option | Markdown | Displayed result |
|--------|---------------------------------------------------|------------------|
| No | `{{</* icon name="dash-circle" */>}} No` | {{< icon name="dash-circle" >}} No |
| Yes | `{{</* icon name="check-circle-filled" */>}} Yes` | {{< icon name="check-circle-filled" >}} Yes |
Do not use these SVG icons in API documentation.
@ -817,8 +822,8 @@ Put the tag at the end of the sentence. Leave one space between the sentence and
For example:
```markdown
| App name | Description |
|:---------|:-------------------------------|
| App name | Description |
|:---------|:------------|
| App A | Description text. <sup>1</sup> |
| App B | Description text. <sup>2</sup> |
```
@ -838,8 +843,8 @@ For example:
The table and footnotes would render as follows:
| App name | Description |
|:---------|:-------------------------------|
| App name | Description |
|:---------|:------------|
| App A | Description text. <sup>1</sup> |
| App B | Description text. <sup>2</sup> |
@ -1493,15 +1498,15 @@ GUI diagramming tools can help authors overcome Mermaid's complexity and layout
the preferred GUI tool because, when using the editor, both the diagram and its definition are
stored in the SVG file, so it can be easily edited. Draw.io is also integrated with the GitLab wiki.
| Feature| Mermaid | Draw.io |
|--------|---------|---------|
| **Editor required** | Text editor | Draw.io editor |
| **WYSIWYG editing** | {{< icon name="dash-circle" >}} No | {{< icon name="check-circle-filled" >}} Yes |
| **Text content findable by `grep`** | {{< icon name="check-circle-filled" >}} Yes | {{< icon name="dash-circle" >}} No |
| **Appearance controlled by** | Web site's CSS | Diagram's author |
| **File format** | SVG | SVG |
| Feature | Mermaid | Draw.io |
|-------------------------------------------|-------------------------------------------------------------------------|---------|
| **Editor required** | Text editor | Draw.io editor |
| **WYSIWYG editing** | {{< icon name="dash-circle" >}} No | {{< icon name="check-circle-filled" >}} Yes |
| **Text content findable by `grep`** | {{< icon name="check-circle-filled" >}} Yes | {{< icon name="dash-circle" >}} No |
| **Appearance controlled by** | Web site's CSS | Diagram's author |
| **File format** | SVG | SVG |
| **VS Code integration (with extensions)** | {{< icon name="check-circle-filled" >}} Yes (Preview and local editing) | {{< icon name="check-circle-filled" >}} Yes (Preview and local editing) |
| **Generated dynamically** | {{< icon name="check-circle-filled" >}} Yes | {{< icon name="dash-circle" >}} No |
| **Generated dynamically** | {{< icon name="check-circle-filled" >}} Yes | {{< icon name="dash-circle" >}} No |
#### Guidelines

View File

@ -7,14 +7,14 @@ title: Export to CSV
This document lists the different implementations of CSV export in GitLab codebase.
| Export type | Implementation | Advantages | Disadvantages | Existing examples |
|---|---|---|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Streaming | - Query and yield data in batches to a response stream.<br>- Download starts immediately. | - Report available immediately. | - No progress indicator.<br>- Requires a reliable connection. | [Export audit event log](../administration/compliance/audit_event_reports.md#exporting-audit-events) |
| Downloading | - Query and write data in batches to a temporary file.<br>- Loads the file into memory.<br>- Sends the file to the client. | - Report available immediately. | - Large amount of data might cause request timeout.<br>- Memory intensive.<br>- Request expires when the user goes to a different page. | - [Export Chain of Custody Report](../user/compliance/compliance_center/compliance_chain_of_custody_report.md)<br>- [Export License Usage File](../subscriptions/self_managed/_index.md#export-your-license-usage) |
| As email attachment | - Asynchronously process the query with background job.<br>- Email uses the export as an attachment. | - Asynchronous processing. | - Requires users use a different app (email) to download the CSV.<br>- Email providers may limit attachment size. | - [Export issues](../user/project/issues/csv_export.md)<br>- [Export merge requests](../user/project/merge_requests/csv_export.md) |
| As downloadable link in email (*) | - Asynchronously process the query with background job.<br>- Email uses an export link. | - Asynchronous processing.<br>- Bypasses email provider attachment size limit. | - Requires users use a different app (email).<br>- Requires additional storage and cleanup. | [Export User Permissions](https://gitlab.com/gitlab-org/gitlab/-/issues/1772) |
| Polling (non-persistent state) | - Asynchronously processes the query with the background job.<br>- Frontend(FE) polls every few seconds to check if CSV file is ready. | - Asynchronous processing.<br>- Automatically downloads to local machine on completion.<br>- In-app solution. | - Non-persistable request - request expires when the user goes to a different page.<br>- API is processed for each polling request. | [Export Vulnerabilities](../user/application_security/vulnerability_report/_index.md#exporting) |
| Polling (persistent state) (*) | - Asynchronously processes the query with background job.<br>- Backend (BE) maintains the export state<br>- FE polls every few seconds to check status.<br>- FE shows 'Download link' when export is ready.<br>- User can download or regenerate a new report. | - Asynchronous processing.<br>- No database calls made during the polling requests (HTTP 304 status is returned until export status changes).<br>- Does not require user to stay on page until export is complete.<br>- In-app solution.<br>- Can be expanded into a generic CSV feature (such as dashboard / CSV API). | - Requires to maintain export states in DB.<br>- Does not automatically download the CSV export to local machine, requires users to select 'Download'. | [Export Merge Commits Report](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43055) |
| Export type | Implementation | Advantages | Disadvantages | Existing examples |
|-----------------------------------|----------------|------------|---------------|-------------------|
| Streaming | - Query and yield data in batches to a response stream.<br>- Download starts immediately. | - Report available immediately. | - No progress indicator.<br>- Requires a reliable connection. | [Export audit event log](../administration/compliance/audit_event_reports.md#exporting-audit-events) |
| Downloading | - Query and write data in batches to a temporary file.<br>- Loads the file into memory.<br>- Sends the file to the client. | - Report available immediately. | - Large amount of data might cause request timeout.<br>- Memory intensive.<br>- Request expires when the user goes to a different page. | - [Export Chain of Custody Report](../user/compliance/compliance_center/compliance_chain_of_custody_report.md)<br>- [Export License Usage File](../subscriptions/self_managed/_index.md#export-your-license-usage) |
| As email attachment | - Asynchronously process the query with background job.<br>- Email uses the export as an attachment. | - Asynchronous processing. | - Requires users use a different app (email) to download the CSV.<br>- Email providers may limit attachment size. | - [Export issues](../user/project/issues/csv_export.md)<br>- [Export merge requests](../user/project/merge_requests/csv_export.md) |
| As downloadable link in email (*) | - Asynchronously process the query with background job.<br>- Email uses an export link. | - Asynchronous processing.<br>- Bypasses email provider attachment size limit. | - Requires users use a different app (email).<br>- Requires additional storage and cleanup. | [Export User Permissions](https://gitlab.com/gitlab-org/gitlab/-/issues/1772) |
| Polling (non-persistent state) | - Asynchronously processes the query with the background job.<br>- Frontend(FE) polls every few seconds to check if CSV file is ready. | - Asynchronous processing.<br>- Automatically downloads to local machine on completion.<br>- In-app solution. | - Non-persistable request - request expires when the user goes to a different page.<br>- API is processed for each polling request. | [Export Vulnerabilities](../user/application_security/vulnerability_report/_index.md#exporting) |
| Polling (persistent state) (*) | - Asynchronously processes the query with background job.<br>- Backend (BE) maintains the export state<br>- FE polls every few seconds to check status.<br>- FE shows 'Download link' when export is ready.<br>- User can download or regenerate a new report. | - Asynchronous processing.<br>- No database calls made during the polling requests (HTTP 304 status is returned until export status changes).<br>- Does not require user to stay on page until export is complete.<br>- In-app solution.<br>- Can be expanded into a generic CSV feature (such as dashboard / CSV API). | - Requires to maintain export states in DB.<br>- Does not automatically download the CSV export to local machine, requires users to select 'Download'. | [Export Merge Commits Report](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43055) |
{{< alert type="note" >}}

View File

@ -89,23 +89,23 @@ Currently only the listed components are available but more components are plann
##### Arguments
| Argument | Description | Type | Required (default value) |
|---|---|---|---|
| `method` | Attribute on the object passed to `gitlab_ui_form_for`. | `Symbol` | `true` |
| `label` | Checkbox label. `label` slot can be used instead of this argument if HTML is needed. | `String` | `false` (`nil`) |
| `help_text` | Help text displayed below the checkbox. `help_text` slot can be used instead of this argument if HTML is needed. | `String` | `false` (`nil`) |
| `checkbox_options` | Options that are passed to [Rails `check_box` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-check_box). | `Hash` | `false` (`{}`) |
| `checked_value` | Value when checkbox is checked. | `String` | `false` (`'1'`) |
| `unchecked_value` | Value when checkbox is unchecked. | `String` | `false` (`'0'`) |
| `label_options` | Options that are passed to [Rails `label` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label). | `Hash` | `false` (`{}`) |
| Argument | Type | Required (default value) | Description |
|--------------------|----------|--------------------------|-------------|
| `method` | `Symbol` | `true` | Attribute on the object passed to `gitlab_ui_form_for`. |
| `label` | `String` | `false` (`nil`) | Checkbox label. `label` slot can be used instead of this argument if HTML is needed. |
| `help_text` | `String` | `false` (`nil`) | Help text displayed below the checkbox. `help_text` slot can be used instead of this argument if HTML is needed. |
| `checkbox_options` | `Hash` | `false` (`{}`) | Options that are passed to [Rails `check_box` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-check_box). |
| `checked_value` | `String` | `false` (`'1'`) | Value when checkbox is checked. |
| `unchecked_value` | `String` | `false` (`'0'`) | Value when checkbox is unchecked. |
| `label_options` | `Hash` | `false` (`{}`) | Options that are passed to [Rails `label` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label). |
##### Slots
This component supports [ViewComponent slots](https://viewcomponent.org/guide/slots.html).
| Slot | Description |
|---|---|
| `label` | Checkbox label content. This slot can be used instead of the `label` argument. |
| Slot | Description |
|-------------|-------------|
| `label` | Checkbox label content. This slot can be used instead of the `label` argument. |
| `help_text` | Help text content displayed below the checkbox. This slot can be used instead of the `help_text` argument. |
#### `gitlab_ui_radio_component`
@ -114,20 +114,20 @@ This component supports [ViewComponent slots](https://viewcomponent.org/guide/sl
##### Arguments
| Argument | Description | Type | Required (default value) |
|---|---|---|---|
| `method` | Attribute on the object passed to `gitlab_ui_form_for`. | `Symbol` | `true` |
| `value` | The value of the radio tag. | `Symbol` | `true` |
| `label` | Radio label. `label` slot can be used instead of this argument if HTML content is needed inside the label. | `String` | `false` (`nil`) |
| `help_text` | Help text displayed below the radio button. `help_text` slot can be used instead of this argument if HTML content is needed inside the help text. | `String` | `false` (`nil`) |
| `radio_options` | Options that are passed to [Rails `radio_button` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-radio_button). | `Hash` | `false` (`{}`) |
| `label_options` | Options that are passed to [Rails `label` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label). | `Hash` | `false` (`{}`) |
| Argument | Type | Required (default value) | Description |
|-----------------|----------|--------------------------|-------------|
| `method` | `Symbol` | `true` | Attribute on the object passed to `gitlab_ui_form_for`. |
| `value` | `Symbol` | `true` | The value of the radio tag. |
| `label` | `String` | `false` (`nil`) | Radio label. `label` slot can be used instead of this argument if HTML content is needed inside the label. |
| `help_text` | `String` | `false` (`nil`) | Help text displayed below the radio button. `help_text` slot can be used instead of this argument if HTML content is needed inside the help text. |
| `radio_options` | `Hash` | `false` (`{}`) | Options that are passed to [Rails `radio_button` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-radio_button). |
| `label_options` | `Hash` | `false` (`{}`) | Options that are passed to [Rails `label` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label). |
##### Slots
This component supports [ViewComponent slots](https://viewcomponent.org/guide/slots.html).
| Slot | Description |
|---|---|
| `label` | Checkbox label content. This slot can be used instead of the `label` argument. |
| Slot | Description |
|-------------|-------------|
| `label` | Checkbox label content. This slot can be used instead of the `label` argument. |
| `help_text` | Help text content displayed below the radio button. This slot can be used instead of the `help_text` argument. |

View File

@ -109,12 +109,12 @@ For example, if you search for more than seven days of data, the API returns onl
The following table shows what type of aggregation is used for each search period:
|Period|Aggregation used|
|---|---|
| Less than 30 minutes | Raw data as ingested |
| More than 30 minutes and less than one hour | By minute |
| More than one hour and less than 72 hours | Hourly |
| More than 72 hours | Daily |
| Period | Aggregation used |
|---------------------------------------------|------------------|
| Less than 30 minutes | Raw data as ingested |
| More than 30 minutes and less than one hour | By minute |
| More than one hour and less than 72 hours | Hourly |
| More than 72 hours | Daily |
### Metrics ingestion limits

View File

@ -113,11 +113,11 @@ The result of a [database migration pipeline](database/database_migration_pipeli
includes the timing information for migrations.
{{< /alert >}}
| Migration Type | Recommended Duration | Notes |
|----|----|---|
| Regular migrations | `<= 3 minutes` | A valid exception are changes without which application functionality or performance would be severely degraded and which cannot be delayed. |
| Post-deployment migrations | `<= 10 minutes` | A valid exception are schema changes, since they must not happen in background migrations. |
| Background migrations | `> 10 minutes` | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any single query must stay below [`1 second` execution time](database/query_performance.md#timing-guidelines-for-queries) with cold caches. |
| Migration Type | Recommended Duration | Notes |
|----------------------------|----------------------|-------|
| Regular migrations | `<= 3 minutes` | A valid exception are changes without which application functionality or performance would be severely degraded and which cannot be delayed. |
| Post-deployment migrations | `<= 10 minutes` | A valid exception are schema changes, since they must not happen in background migrations. |
| Background migrations | `> 10 minutes` | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any single query must stay below [`1 second` execution time](database/query_performance.md#timing-guidelines-for-queries) with cold caches. |
## Large Tables Limitations

View File

@ -85,11 +85,11 @@ make it unnoticeable for users, because we don't want them to receive `404 Not F
if we can avoid it. This table describes the minimum required in different
cases:
| URL description | Example | What to do |
|---|---|---|
| Can be used in scripts and automation | `snippet#raw` | Support both an old and new URL for one major release. Then, support a redirect from an old URL to a new URL for another major release. |
| Likely to be saved or shared | `issue#show` | Add a redirect from an old URL to a new URL until the next major release. |
| Limited use, unlikely to be shared | `admin#labels` | No extra steps required. |
| URL description | Example | What to do |
|---------------------------------------|----------------|------------|
| Can be used in scripts and automation | `snippet#raw` | Support both an old and new URL for one major release. Then, support a redirect from an old URL to a new URL for another major release. |
| Likely to be saved or shared | `issue#show` | Add a redirect from an old URL to a new URL until the next major release. |
| Limited use, unlikely to be shared | `admin#labels` | No extra steps required. |
In all cases, an old route should only be removed once traffic to it has
dropped sufficiently (for instance, according to logs or BigQuery). Otherwise, more

View File

@ -14,24 +14,24 @@ goal of reducing the number of vulnerabilities released over time.
For each of the vulnerabilities listed in this document, AppSec aims to have a SAST rule either in the form of a semgrep rule (or a RuboCop rule) that runs in the CI pipeline. Below is a table of all existing guidelines and their coverage status:
| Guideline | Rule | Status |
|---|---|---|
| [Regular Expressions](#regular-expressions-guidelines) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_regex.yml) | ✅ |
| [ReDOS](#denial-of-service-redos--catastrophic-backtracking) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_redos_1.yml), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_redos_2.yml), [3](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/merge_requests/59#note_2443657926) | ✅ |
| [JWT](#json-web-tokens-jwt) | Pending | ❌ |
| [SSRF](#server-side-request-forgery-ssrf) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_url-1.yml), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_http.yml?ref_type=heads) | ✅ |
| [XSS](#xss-guidelines) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xss_redirect.yml), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xss_html_safe.yml) | ✅ |
| [XXE](#xml-external-entities) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_change_unsafe_nokogiri_parse_option.yml?ref_type=heads), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_initialize_unsafe_nokogiri_parse_option.yml?ref_type=heads), [3](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_set_unsafe_nokogiri_parse_option.yml?ref_type=heads), [4](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_unsafe_xml_libraries.yml?ref_type=heads) | ✅ |
| [Path traversal](#path-traversal-guidelines) (Ruby) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_path_traversal.yml?ref_type=heads) | ✅ |
| [Path traversal](#path-traversal-guidelines) (Go) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/merge_requests/39) | ✅ |
| [OS command injection](#os-command-injection-guidelines) (Ruby) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_command_injection.yml?ref_type=heads) | ✅ |
| [OS command injection](#os-command-injection-guidelines) (Go) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/go/go_dangerous_exec_command.yml?ref_type=heads) | ✅ |
| [Insecure TLS ciphers](#tls-minimum-recommended-version) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_ciphers.yml?ref_type=heads) | ✅ |
| [Archive operations](#working-with-archive-files) (Ruby) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_archive_operations.yml?ref_type=heads) | ✅ |
| [Archive operations](#working-with-archive-files) (Go) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/go/go_insecure_archive_operations.yml) | ✅ |
| [URL spoofing](#url-spoofing) | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_url_spoofing.yml) | ✅ |
| [Request Parameter Typing](#request-parameter-typing) | `StrongParams` RuboCop | ✅ |
| [Paid tiers for vulnerability mitigation](#paid-tiers-for-vulnerability-mitigation) | N/A <!-- This cannot be validated programmatically //--> | |
| Guideline | Status | Rule |
|-------------------------------------------------------------------------------------|--------|------|
| [Regular Expressions](#regular-expressions-guidelines) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_regex.yml) |
| [ReDOS](#denial-of-service-redos--catastrophic-backtracking) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_redos_1.yml), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_redos_2.yml), [3](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/merge_requests/59#note_2443657926) |
| [JWT](#json-web-tokens-jwt) | ❌ | Pending |
| [SSRF](#server-side-request-forgery-ssrf) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_url-1.yml), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_http.yml?ref_type=heads) |
| [XSS](#xss-guidelines) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xss_redirect.yml), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xss_html_safe.yml) |
| [XXE](#xml-external-entities) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_change_unsafe_nokogiri_parse_option.yml?ref_type=heads), [2](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_initialize_unsafe_nokogiri_parse_option.yml?ref_type=heads), [3](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_set_unsafe_nokogiri_parse_option.yml?ref_type=heads), [4](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_xml_injection_unsafe_xml_libraries.yml?ref_type=heads) |
| [Path traversal](#path-traversal-guidelines) (Ruby) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_path_traversal.yml?ref_type=heads) |
| [Path traversal](#path-traversal-guidelines) (Go) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/merge_requests/39) |
| [OS command injection](#os-command-injection-guidelines) (Ruby) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_command_injection.yml?ref_type=heads) |
| [OS command injection](#os-command-injection-guidelines) (Go) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/go/go_dangerous_exec_command.yml?ref_type=heads) |
| [Insecure TLS ciphers](#tls-minimum-recommended-version) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_ciphers.yml?ref_type=heads) |
| [Archive operations](#working-with-archive-files) (Ruby) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_insecure_archive_operations.yml?ref_type=heads) |
| [Archive operations](#working-with-archive-files) (Go) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/go/go_insecure_archive_operations.yml) |
| [URL spoofing](#url-spoofing) | ✅ | [1](https://gitlab.com/gitlab-com/gl-security/product-security/appsec/sast-custom-rules/-/blob/main/secure-coding-guidelines/ruby/ruby_url_spoofing.yml) |
| [Request Parameter Typing](#request-parameter-typing) | ✅ | `StrongParams` RuboCop |
| [Paid tiers for vulnerability mitigation](#paid-tiers-for-vulnerability-mitigation) | | N/A <!-- This cannot be validated programmatically //--> |
## Process for creating new guidelines and accompanying rules

View File

@ -136,11 +136,11 @@ metadata from the uploaded file. There are a couple of different ways
you can implement this. The main choice is _where_ to implement the
processing, or "who is the processor".
|Processor|Direct Upload possible?|Can reject HTTP request?|Implementation|
|---|---|---|---|
|Sidekiq|yes|no|Straightforward|
|Workhorse|yes|yes|Complex|
|Rails|no|yes|Easy|
| Processor | Direct Upload possible? | Can reject HTTP request? | Implementation |
|-----------|-------------------------|--------------------------|----------------|
| Sidekiq | yes | no | Straightforward |
| Workhorse | yes | yes | Complex |
| Rails | no | yes | Easy |
Processing in Rails looks appealing but it tends to lead to scaling
problems down the road because you cannot use direct upload. You are
@ -210,10 +210,10 @@ pre-processing behaviors are skipped silently.
CarrierWave has 2 storage engines:
|CarrierWave class|GitLab name|Description|
|---|---|---|
|`CarrierWave::Storage::File`|`ObjectStorage::Store::LOCAL` |Local files, accessed through the Ruby `stdlib` |
| `CarrierWave::Storage::Fog`|`ObjectStorage::Store::REMOTE`|Cloud files, accessed through the [Fog gem](https://github.com/fog/fog)|
| CarrierWave class | GitLab name | Description |
|------------------------------|--------------------------------|-------------|
| `CarrierWave::Storage::File` | `ObjectStorage::Store::LOCAL` | Local files, accessed through the Ruby `stdlib` |
| `CarrierWave::Storage::Fog` | `ObjectStorage::Store::REMOTE` | Cloud files, accessed through the [Fog gem](https://github.com/fog/fog) |
GitLab uses both of these engines, depending on configuration.

View File

@ -168,12 +168,12 @@ Hovering over each bar reveals a dialog that explains the score's definition.
For example, if a project has a high score for deployment frequency (velocity), it means that the project has one or more deploys to production per day.
| Metric | Description | High | Medium | Low |
|--------|-------------|------|--------|-----|
| Deployment frequency | The number of deploys to production per day | ≥30 | 1-29 | \<1 |
| Lead time for changes | The number of days to go from code committed to code successfully running in production| ≤7 | 8-29 | ≥30 |
| Time to restore service | The number of days to restore service when a service incident or a defect that impacts users occurs | ≤1 | 2-6 | ≥7 |
| Change failure rate | The percentage of changes to production resulted in degraded service | ≤15% | 16%-44% | ≥45% |
| Metric | High | Medium | Low | Description |
|-------------------------|------|---------|------|-------------|
| Deployment frequency | ≥30 | 1-29 | \<1 | The number of deploys to production per day |
| Lead time for changes | ≤7 | 8-29 | ≥30 | The number of days to go from code committed to code successfully running in production |
| Time to restore service | ≤1 | 2-6 | ≥7 | The number of days to restore service when a service incident or a defect that impacts users occurs |
| Change failure rate | ≤15% | 16%-44% | ≥45% | The percentage of changes to production resulted in degraded service |
To learn more, see the blog post [Inside DORA Performers score in GitLab Value Streams Dashboard](https://about.gitlab.com/blog/2024/01/18/inside-dora-performers-score-in-gitlab-value-streams-dashboard/).
@ -322,18 +322,18 @@ After you have set up the project, set up the configuration file:
1. In the default branch, create the configuration file: `.gitlab/analytics/dashboards/value_streams/value_streams.yaml`.
1. In the `value_streams.yaml` configuration file, fill in the configuration options:
|Field|Description|
|---|---|
| `title` | Custom name for the panel |
|`queryOverrides` (formerly `data`) | Overrides data query parameters specific to each visualization. |
|`namespace` (subfield of `queryOverrides`) | Group or project path to use for the panel |
|`filters` (subfield of `queryOverrides`) | Filters the query for each visualization type. See [supported visualizations](#supported-visualization-filters). |
| `visualization` | The type of visualization to be rendered. Supported options are `dora_chart`, `dora_performers_score`, and `usage_overview`. |
| `gridAttributes` | The size and positioning of the panel |
| `xPos` (subfield of `gridAttributes`) | Horizontal position of the panel |
| `yPos` (subfield of `gridAttributes`) | Vertical position of the panel |
| `width` (subfield of `gridAttributes`) | Width of the panel (max. 12) |
| `height` (subfield of `gridAttributes`) | Height of the panel |
| Field | Description |
|--------------------------------------------|-------------|
| `title` | Custom name for the panel |
| `queryOverrides` (formerly `data`) | Overrides data query parameters specific to each visualization. |
| `namespace` (subfield of `queryOverrides`) | Group or project path to use for the panel |
| `filters` (subfield of `queryOverrides`) | Filters the query for each visualization type. See [supported visualizations](#supported-visualization-filters). |
| `visualization` | The type of visualization to be rendered. Supported options are `dora_chart`, `dora_performers_score`, and `usage_overview`. |
| `gridAttributes` | The size and positioning of the panel |
| `xPos` (subfield of `gridAttributes`) | Horizontal position of the panel |
| `yPos` (subfield of `gridAttributes`) | Vertical position of the panel |
| `width` (subfield of `gridAttributes`) | Width of the panel (max. 12) |
| `height` (subfield of `gridAttributes`) | Height of the panel |
```yaml
# version - The latest version of the analytics dashboard schema
@ -413,18 +413,18 @@ The `filters` subfield on the `queryOverrides` field can be used to customize th
Filters for the `dora_chart` visualization.
|Filter|Description|Supported values|
|---|---|---|
|`excludeMetrics`| Hides rows by metric ID from the chart panel | `deployment_frequency`, `lead_time_for_changes`,`time_to_restore_service`, `change_failure_rate`, `lead_time`, `cycle_time`, `issues`, `issues_completed`, `deploys`, `merge_request_throughput`, `median_time_to_merge`, `contributor_count`, `vulnerability_critical`, `vulnerability_high`|
| `labels` | Filters data by labels | Any available group label. Label filtering is supported by the following metrics: `lead_time`, `cycle_time`, `issues`, `issues_completed`, `merge_request_throughput`, `median_time_to_merge`. |
| Filter | Description | Supported values |
|------------------|----------------------------------------------|------------------|
| `excludeMetrics` | Hides rows by metric ID from the chart panel | `deployment_frequency`, `lead_time_for_changes`,`time_to_restore_service`, `change_failure_rate`, `lead_time`, `cycle_time`, `issues`, `issues_completed`, `deploys`, `merge_request_throughput`, `median_time_to_merge`, `contributor_count`, `vulnerability_critical`, `vulnerability_high` |
| `labels` | Filters data by labels | Any available group label. Label filtering is supported by the following metrics: `lead_time`, `cycle_time`, `issues`, `issues_completed`, `merge_request_throughput`, `median_time_to_merge`. |
#### DORA Performers score panel filters
Filters for the `dora_performers_score` visualization.
|Filter|Description|Supported values|
|---|---|---|
|`projectTopics`|Filters the projects shown based on their assigned [topics](../project/project_topics.md)| Any available group topic|
| Filter | Description | Supported values |
|-----------------|-------------------------------------------------------------------------------------------|------------------|
| `projectTopics` | Filters the projects shown based on their assigned [topics](../project/project_topics.md) | Any available group topic |
#### Usage overview panel filters
@ -432,34 +432,34 @@ Filters for the `usage_overview` visualization.
##### Group and subgroup namespaces
|Filter|Description|Supported values|
|---|---|---|
|`include`|Limits the metrics returned, by default displays all available| `groups`, `projects`, `issues`, `merge_requests`, `pipelines`, `users`|
| Filter | Description | Supported values |
|-----------|----------------------------------------------------------------|------------------|
| `include` | Limits the metrics returned, by default displays all available | `groups`, `projects`, `issues`, `merge_requests`, `pipelines`, `users` |
##### Project namespaces
|Filter|Description|Supported values|
|---|---|---|
|`include`|Limits the metrics returned, by default displays all available| `issues`, `merge_requests`, `pipelines`|
| Filter | Description | Supported values |
|-----------|----------------------------------------------------------------|------------------|
| `include` | Limits the metrics returned, by default displays all available | `issues`, `merge_requests`, `pipelines` |
## Dashboard metrics and drill-down reports
| Metric | Description | Drill-down report | Documentation page | ID |
| ------ | ----------- | --------------- | ------------------ | -- |
| Deployment frequency | Average number of deployments to production per day. This metric measures how often value is delivered to end users. | [Deployment frequency tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=deployment-frequency) | [Deployment frequency](dora_metrics.md#deployment-frequency) | `deployment_frequency` |
| Lead time for changes | The time to successfully deliver a commit into production. This metric reflects the efficiency of CI/CD pipelines. | [Lead time tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=lead-time) | [Lead time for changes](dora_metrics.md#lead-time-for-changes) | `lead_time_for_changes` |
| Time to restore service | The time it takes an organization to recover from a failure in production. | [Time to restore service tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=time-to-restore-service) | [Time to restore service](dora_metrics.md#time-to-restore-service) | `time_to_restore_service` |
| Change failure rate | Percentage of deployments that cause an incident in production. | [Change failure rate tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=change-failure-rate) | [Change failure rate](dora_metrics.md#change-failure-rate) | `change_failure_rate` |
| Lead time | Median time from issue created to issue closed. | [Value Stream Analytics](https://gitlab.com/groups/gitlab-org/-/analytics/value_stream_analytics) | [View the lead time and cycle time for issues](../group/value_stream_analytics/_index.md#lifecycle-metrics) | `lead_time` |
| Cycle time | Median time from the earliest commit of a linked issue's merge request to when that issue is closed. | [VSA overview](https://gitlab.com/groups/gitlab-org/-/analytics/value_stream_analytics) | [View the lead time and cycle time for issues](../group/value_stream_analytics/_index.md#lifecycle-metrics) | `cycle_time` |
| Issues created | Number of new issues created. | [Issue Analytics](https://gitlab.com/groups/gitlab-org/-/issues_analytics) | [Issue Analytics](../group/issues_analytics/_index.md) | `issues` |
| Issues closed | Number of issues closed by month. | [Issue Analytics](https://gitlab.com/groups/gitlab-org/-/issues_analytics) | [Issue Analytics](../group/issues_analytics/_index.md) | `issues_completed` |
| Number of deploys | Total number of deploys to production. | [Merge Request Analytics](https://gitlab.com/gitlab-org/gitlab/-/analytics/merge_request_analytics) | [Merge request analytics](merge_request_analytics.md) | `deploys` |
| Merge request throughput | The number of merge requests merged by month. | [Groups Productivity analytics](productivity_analytics.md), [Projects Merge Request Analytics](https://gitlab.com/gitlab-org/gitlab/-/analytics/merge_request_analytics) | [Groups Productivity analytics](productivity_analytics.md) [Projects Merge request analytics](merge_request_analytics.md) | `merge_request_throughput` |
| Median time to merge | Median time between merge request created and merge request merged. | [Groups Productivity analytics](productivity_analytics.md), [Projects Merge Request Analytics](https://gitlab.com/gitlab-org/gitlab/-/analytics/merge_request_analytics) | [Groups Productivity analytics](productivity_analytics.md) [Projects Merge request analytics](merge_request_analytics.md) | `median_time_to_merge` |
| Contributor count | Number of monthly unique users with contributions in the group.| [Contribution Analytics](https://gitlab.com/groups/gitlab-org/-/contribution_analytics) | [User contribution events](../profile/contributions_calendar.md#user-contribution-events) | `contributor_count` |
| Critical vulnerabilities over time | Critical vulnerabilities over time in project or group | [Vulnerability report](https://gitlab.com/gitlab-org/gitlab/-/security/vulnerability_report) | [Vulnerability report](../application_security/vulnerability_report/_index.md) | `vulnerability_critical` |
| High vulnerabilities over time | High vulnerabilities over time in project or group | [Vulnerability report](https://gitlab.com/gitlab-org/gitlab/-/security/vulnerability_report) | [Vulnerability report](../application_security/vulnerability_report/_index.md) | `vulnerability_high` |
| Metric | ID | Description | Drill-down report | Documentation page |
|------------------------------------|----------------------------|-------------|-------------------|--------------------|
| Deployment frequency | `deployment_frequency` | Average number of deployments to production per day. This metric measures how often value is delivered to end users. | [Deployment frequency tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=deployment-frequency) | [Deployment frequency](dora_metrics.md#deployment-frequency) |
| Lead time for changes | `lead_time_for_changes` | The time to successfully deliver a commit into production. This metric reflects the efficiency of CI/CD pipelines. | [Lead time tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=lead-time) | [Lead time for changes](dora_metrics.md#lead-time-for-changes) |
| Time to restore service | `time_to_restore_service` | The time it takes an organization to recover from a failure in production. | [Time to restore service tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=time-to-restore-service) | [Time to restore service](dora_metrics.md#time-to-restore-service) |
| Change failure rate | `change_failure_rate` | Percentage of deployments that cause an incident in production. | [Change failure rate tab](https://gitlab.com/groups/gitlab-org/-/analytics/ci_cd?tab=change-failure-rate) | [Change failure rate](dora_metrics.md#change-failure-rate) |
| Lead time | `lead_time` | Median time from issue created to issue closed. | [Value Stream Analytics](https://gitlab.com/groups/gitlab-org/-/analytics/value_stream_analytics) | [View the lead time and cycle time for issues](../group/value_stream_analytics/_index.md#lifecycle-metrics) |
| Cycle time | `cycle_time` | Median time from the earliest commit of a linked issue's merge request to when that issue is closed. | [VSA overview](https://gitlab.com/groups/gitlab-org/-/analytics/value_stream_analytics) | [View the lead time and cycle time for issues](../group/value_stream_analytics/_index.md#lifecycle-metrics) |
| Issues created | `issues` | Number of new issues created. | [Issue Analytics](https://gitlab.com/groups/gitlab-org/-/issues_analytics) | [Issue Analytics](../group/issues_analytics/_index.md) |
| Issues closed | `issues_completed` | Number of issues closed by month. | [Issue Analytics](https://gitlab.com/groups/gitlab-org/-/issues_analytics) | [Issue Analytics](../group/issues_analytics/_index.md) |
| Number of deploys | `deploys` | Total number of deploys to production. | [Merge Request Analytics](https://gitlab.com/gitlab-org/gitlab/-/analytics/merge_request_analytics) | [Merge request analytics](merge_request_analytics.md) |
| Merge request throughput | `merge_request_throughput` | The number of merge requests merged by month. | [Groups Productivity analytics](productivity_analytics.md), [Projects Merge Request Analytics](https://gitlab.com/gitlab-org/gitlab/-/analytics/merge_request_analytics) | [Groups Productivity analytics](productivity_analytics.md) [Projects Merge request analytics](merge_request_analytics.md) |
| Median time to merge | `median_time_to_merge` | Median time between merge request created and merge request merged. | [Groups Productivity analytics](productivity_analytics.md), [Projects Merge Request Analytics](https://gitlab.com/gitlab-org/gitlab/-/analytics/merge_request_analytics) | [Groups Productivity analytics](productivity_analytics.md) [Projects Merge request analytics](merge_request_analytics.md) |
| Contributor count | `contributor_count` | Number of monthly unique users with contributions in the group. | [Contribution Analytics](https://gitlab.com/groups/gitlab-org/-/contribution_analytics) | [User contribution events](../profile/contributions_calendar.md#user-contribution-events) |
| Critical vulnerabilities over time | `vulnerability_critical` | Critical vulnerabilities over time in project or group | [Vulnerability report](https://gitlab.com/gitlab-org/gitlab/-/security/vulnerability_report) | [Vulnerability report](../application_security/vulnerability_report/_index.md) |
| High vulnerabilities over time | `vulnerability_high` | High vulnerabilities over time in project or group | [Vulnerability report](https://gitlab.com/gitlab-org/gitlab/-/security/vulnerability_report) | [Vulnerability report](../application_security/vulnerability_report/_index.md) |
## Metrics with Jira

View File

@ -21,12 +21,12 @@ An organization that's interested in using a software product may require an SBO
If you're familiar with the GitLab package registry, you might wonder what the difference is between an SBOM and a [dependency list](../../application_security/dependency_list/_index.md). The following table highlights the key differences:
| Differences | Dependency list | SBOM |
|---|---|---|
| **Scope** | Shows dependencies for individual projects. | Creates an inventory of all packages published across your group. |
| **Direction** | Tracks what your projects depend on (incoming dependencies). | Tracks what your group publishes (outgoing packages). |
| **Coverage** | Based on package manifests, like `package.json` or `pom.xml`. | Covers actual published artifacts in your package registry. |
| **Format** | GitLab-specific feature. | Generates standard CycloneDX SBOMs that can be used with external tools. |
| Differences | Dependency list | SBOM |
|---------------|---------------------------------------------------------------|------|
| **Scope** | Shows dependencies for individual projects. | Creates an inventory of all packages published across your group. |
| **Direction** | Tracks what your projects depend on (incoming dependencies). | Tracks what your group publishes (outgoing packages). |
| **Coverage** | Based on package manifests, like `package.json` or `pom.xml`. | Covers actual published artifacts in your package registry. |
| **Format** | GitLab-specific feature. | Generates standard CycloneDX SBOMs that can be used with external tools. |
## What is CycloneDX?

View File

@ -83,13 +83,13 @@ For example, you can use code similar to the following to add the test coverage
The following table shows the default test coverage limits and badge colors:
| Test coverage | Percentage limits | Badge color |
|---|---|---|
| Good | 95 up to and including 100% | <span style="color: #4c1"></span> `#4c1` |
| Acceptable | 90 up to 95% | <span style="color:#a3c51c"></span> `#a3c51c` |
| Medium | 75 up to 90% | <span style="color: #dfb317"></span> `#dfb317` |
| Low | 0 up to 75% | <span style="color: #e05d44"></span> `#e05d44` |
| Unknown | No coverage | <span style="color: #9f9f9f"></span> `#9f9f9f` |
| Test coverage | Percentage limits | Badge color |
|---------------|-----------------------------|-------------|
| Good | 95 up to and including 100% | <span style="color: #4c1"></span> `#4c1` |
| Acceptable | 90 up to 95% | <span style="color:#a3c51c"></span> `#a3c51c` |
| Medium | 75 up to 90% | <span style="color: #dfb317"></span> `#dfb317` |
| Low | 0 up to 75% | <span style="color: #e05d44"></span> `#e05d44` |
| Unknown | No coverage | <span style="color: #9f9f9f"></span> `#9f9f9f` |
{{< alert type="note" >}}
@ -101,11 +101,11 @@ The following table shows the default test coverage limits and badge colors:
You can override the default limits by passing the following query parameters in the coverage report badge URL:
| Query parameter | Acceptable values | Default |
|---|---|---|
| `min_good` | Any value between 3 and 100 | 95 |
| `min_acceptable` | Any value between 2 and `min_good`1 | 90 |
| `min_medium` | Any value between 1 and `min_acceptable`1 | 75 |
| Query parameter | Acceptable values | Default |
|------------------|----------------------------------------------|---------|
| `min_good` | Any value between `3` and `100` | `95` |
| `min_acceptable` | Any value between `2` and `min_good`1 | `90` |
| `min_medium` | Any value between `1` and `min_acceptable`1 | `75` |
For example:

View File

@ -2,9 +2,9 @@
module ActiveContext
module CollectionCache
class << self
TTL = 1.minute
TTL = 1.minute
class << self
def collections
refresh_cache if cache_expired?
@ -25,10 +25,14 @@ module ActiveContext
private
def ttl
Rails.env.test? ? 0.seconds : TTL
end
def cache_expired?
return true unless @last_refreshed_at
Time.current - @last_refreshed_at > TTL
Time.current - @last_refreshed_at > ttl
end
def refresh_cache

View File

@ -83,7 +83,9 @@ module ActiveContext
def references
reference_klasses = Array.wrap(self.class.reference_klasses)
routing = self.class.routing(object)
collection_id = self.class.collection_record.id
collection_id = self.class.collection_record&.id
raise StandardError, "#{self.class} expected to have a collection record" unless collection_id
reference_klasses.map do |reference_klass|
reference_klass.serialize(collection_id: collection_id, routing: routing, data: object)

View File

@ -18,10 +18,6 @@ module ActiveContext
@authorized_results ||= collection.redact_unauthorized_results!(self)
end
def ids
each.pluck('ref_id')
end
def each
raise NotImplementedError
end

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
module ActiveContext
module Preprocessors
module ContentFetcher
extend ActiveSupport::Concern
ContentNotFoundError = Class.new(StandardError)
class_methods do
def fetch_content(refs:, query:, collection:, content_field: 'content')
matches = ::ActiveContext.adapter.client.search(
user: nil,
collection: collection,
query: query
)
content_by_id = matches.each_with_object({}) do |match, hash|
hash[match['id']] = match[content_field]
end
with_per_ref_handling(refs, error_types: [ContentNotFoundError]) do |ref|
unless content_by_id.key?(ref.identifier)
raise ContentNotFoundError, "content not found for chunk with id: #{ref.identifier}"
end
ref.documents << { content: content_by_id[ref.identifier] }
ref
end
end
end
end
end
end

View File

@ -5,6 +5,7 @@ module ActiveContext
extend Concerns::ReferenceUtils
extend Concerns::Preprocessor
include Preprocessors::Chunking
include Preprocessors::ContentFetcher
include Preprocessors::Embeddings
include Preprocessors::Preload
@ -48,7 +49,7 @@ module ActiveContext
@routing = routing
@serialized_args = Array(args)
@ref_version = Time.now.to_i
@include_ref_fields = collection.include_ref_fields
@include_ref_fields = @collection.respond_to?(:include_ref_fields) ? @collection.include_ref_fields : true
init
end

View File

@ -33,7 +33,7 @@ RSpec.describe ActiveContext::Databases::Concerns::Adapter do
end
let(:connection) { double('Connection') }
let(:options) { { host: 'localhost' } }
let(:options) { { 'host' => 'localhost' } }
subject(:adapter) { test_class.new(connection, options: options) }

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
RSpec.describe ActiveContext::Preprocessors::ContentFetcher do
let(:reference_class) do
Class.new(Test::References::Mock) do
include ::ActiveContext::Preprocessors::ContentFetcher
add_preprocessor :fetch_content do |refs|
fetch_content(refs: refs, query: '*', collection: 'mock_collection')
end
end
end
let(:reference_1) { reference_class.new(collection_id: collection_id, routing: partition, args: 'id1') }
let(:reference_2) { reference_class.new(collection_id: collection_id, routing: partition, args: 'id2') }
let(:mock_adapter) { double }
let(:mock_collection) { double(name: collection_name, partition_for: partition, include_ref_fields: true) }
let(:mock_connection) { double(id: connection_id) }
let(:query) { '*' }
let(:connection_id) { 3 }
let(:partition) { 2 }
let(:collection_id) { 1 }
let(:collection_name) { 'mock_collection' }
let(:search_results) do
[
{ 'id' => 'id1', 'content' => 'Content for document 1' },
{ 'id' => 'id2', 'content' => 'Content for document 2' }
]
end
subject(:process_refs) { ActiveContext::Reference.preprocess_references([reference_1, reference_2]) }
before do
allow(ActiveContext).to receive(:adapter).and_return(mock_adapter)
allow(mock_adapter).to receive_message_chain(:client, :search).and_return(search_results)
allow(ActiveContext::CollectionCache).to receive(:fetch).and_return(mock_collection)
allow(ActiveContext::Logger).to receive(:exception).and_return(nil)
end
describe '.fetch_content' do
context 'when content is found for all references' do
it 'fetches content and adds it to reference documents' do
process_refs
expect(reference_1.documents).to include({ content: 'Content for document 1' })
expect(reference_2.documents).to include({ content: 'Content for document 2' })
end
it 'calls search with the correct parameters' do
expect(mock_adapter).to receive_message_chain(:client, :search).with(
user: nil,
collection: collection_name,
query: query
)
process_refs
end
it 'returns the references' do
result = process_refs
expect(result[:successful]).to eq([reference_1, reference_2])
expect(result[:failed]).to be_empty
end
end
context 'when content is not found for some references' do
let(:search_results) do
[
{ 'id' => 'id1', 'content' => 'Content for document 1' }
]
end
it 'adds the ref to the failed refs result', :aggregate_failures do
result = process_refs
expect(result[:successful]).to eq([reference_1])
expect(result[:failed]).to eq([reference_2])
end
end
end
end

View File

@ -52,7 +52,9 @@ module API
end
def authorize_ai_access!
forbidden!('Insufficient permissions for Duo Workflow') unless can?(current_user, :duo_workflow, user_project)
return if can?(current_user, :duo_workflow, user_project) || can?(current_user, :access_duo_agentic_chat, user_project)
forbidden!('Insufficient permissions for Duo Workflow')
end
def commit_response(attrs)

View File

@ -44801,6 +44801,12 @@ msgstr ""
msgid "PipelineScheduleIntervalPattern|Custom"
msgstr ""
msgid "PipelineScheduleIntervalPattern|Learn more."
msgstr ""
msgid "PipelineScheduleIntervalPattern|Pipelines cannot run more frequently than the pipeline schedule worker cron setting (%{workerCronExpression}) allows."
msgstr ""
msgid "PipelineScheduleIntervalPattern|Set a custom interval with Cron syntax."
msgstr ""

View File

@ -3,13 +3,39 @@
require 'spec_helper'
RSpec.describe 'Bizible content security policy', feature_category: :subscription_management do
include ContentSecurityPolicyHelpers
let(:bizible_enabled) { true }
let(:csp) { ActionDispatch::ContentSecurityPolicy.new { |p| p.default_src '' } }
let(:script_src) { ["'unsafe-eval'", 'https://cdn.bizible.com/scripts/bizible.js'] }
subject(:csp_header) { response_headers['Content-Security-Policy'] }
before do
stub_config(extra: { one_trust_id: SecureRandom.uuid })
stub_config(extra: { bizible: bizible_enabled })
stub_feature_flags(ecomm_instrumentation: true)
stub_csp_for_controller(RegistrationsController, csp)
visit new_user_registration_path
end
it 'has proper Content Security Policy headers' do
visit root_path
expect(find_csp_directive('script-src', header: csp_header)).to include(*script_src)
end
expect(response_headers['Content-Security-Policy']).to include('https://cdn.bizible.com/scripts/bizible.js')
context 'when disabled' do
let(:bizible_enabled) { false }
it 'does not have Content Security Policy headers' do
expect(csp_header).not_to include(*script_src)
end
end
context 'when CSP is absent' do
let(:csp) { ActionDispatch::ContentSecurityPolicy.new }
it 'does not have Content Security Policy headers' do
expect(csp_header).not_to include(*script_src)
end
end
end

View File

@ -82,5 +82,92 @@ RSpec.describe WorkItems::WorkItemsFinder, feature_category: :team_planning do
end
end
end
context 'when using parent_ids filter' do
let(:scope) { 'all' }
context 'when user has access to child item' do
let_it_be(:child_item1) { create(:work_item, project: project1) }
let_it_be(:parent_item1) { create(:work_item, :epic, project: project1) }
let(:params) { { work_item_parent_ids: [parent_item1.id] } }
before do
create(:parent_link, work_item_parent: parent_item1, work_item: child_item1)
end
it 'returns corresponding child work items' do
expect(items).to contain_exactly(child_item1)
end
end
context 'when filtering by parent item from different project' do
let_it_be(:another_project) { create(:project) }
let_it_be(:child_item2) { create(:work_item, project: project1) }
let_it_be(:parent_item2) { create(:work_item, :epic, project: another_project) }
let(:params) { { work_item_parent_ids: [parent_item2.id] } }
before do
create(:parent_link, work_item_parent: parent_item2, work_item: child_item2)
end
it 'returns corresponding child work items' do
expect(items).to contain_exactly(child_item2)
end
end
context 'when filtering by multiple parent items' do
let_it_be(:child_item3) { create(:work_item, project: project1) }
let_it_be(:child_item4) { create(:work_item, project: project1) }
let_it_be(:parent_item3) { create(:work_item, :epic, project: project1) }
let_it_be(:parent_item4) { create(:work_item, :epic, project: project1) }
let(:params) { { work_item_parent_ids: [parent_item3.id, parent_item4.id] } }
before do
create(:parent_link, work_item_parent: parent_item3, work_item: child_item3)
create(:parent_link, work_item_parent: parent_item4, work_item: child_item4)
end
it 'returns corresponding child work items' do
expect(items).to contain_exactly(child_item3, child_item4)
end
end
context 'when user does not have access to child items' do
let_it_be(:confidential_work_item) { create(:work_item, confidential: true, project: project1) }
let_it_be(:parent_item5) { create(:work_item, :epic, confidential: true, project: project1) }
let(:search_user) { user2 }
let(:params) { { work_item_parent_ids: [parent_item5.id] } }
before do
create(:parent_link, work_item_parent: parent_item5, work_item: confidential_work_item)
end
it 'does not return those items' do
expect(items).to be_empty
end
end
context 'when user does not have access to child and parent items' do
let_it_be(:private_project) { create(:project, :private) }
let_it_be(:private_work_item) { create(:work_item, project: private_project) }
let_it_be(:private_parent_item) { create(:work_item, :epic, project: private_project) }
let(:search_user) { user2 }
let(:params) { { work_item_parent_ids: [private_parent_item.id] } }
before do
create(:parent_link, work_item_parent: private_parent_item, work_item: private_work_item)
end
it 'does not return those items' do
expect(items).to be_empty
end
end
end
end
end

View File

@ -20,6 +20,7 @@ describe('Interval Pattern Input Component', () => {
const customKey = 'custom';
const everyDayKey = 'everyDay';
const cronIntervalNotInPreset = `0 12 * * *`;
const workerCronValue = '3-59/10 * * * *';
const findEveryDayRadio = () => wrapper.findByTestId(everyDayKey);
const findEveryWeekRadio = () => wrapper.findByTestId('everyWeek');
@ -35,6 +36,7 @@ describe('Interval Pattern Input Component', () => {
const selectEveryWeekRadio = () => findEveryWeekRadio().setChecked(true);
const selectEveryMonthRadio = () => findEveryMonthRadio().setChecked(true);
const selectCustomRadio = () => findCustomRadio().setChecked(true);
const findWorkerCronExpressionHint = () => wrapper.findByTestId('worker-cron-expression-hint');
const createWrapper = (props = {}, data = {}) => {
wrapper = mountExtended(IntervalPatternInput, {
@ -47,6 +49,9 @@ describe('Interval Pattern Input Component', () => {
randomDay: mockDay,
};
},
provide: {
workerCronExpression: workerCronValue,
},
});
};
@ -64,6 +69,17 @@ describe('Interval Pattern Input Component', () => {
window.gl = oldWindowGl;
});
describe('the worker cron expression hint', () => {
beforeEach(() => {
createWrapper();
});
it('displays the expected value', () => {
expect(findWorkerCronExpressionHint().exists()).toBe(true);
expect(findWorkerCronExpressionHint().text()).toContain(workerCronValue);
});
});
describe('the input field defaults', () => {
beforeEach(() => {
createWrapper();

View File

@ -161,6 +161,35 @@ RSpec.describe Resolvers::WorkItemsResolver, feature_category: :team_planning do
expect(batch_sync { resolve_items(iids: iids).to_a }).to contain_exactly(item1, item2)
end
context 'with parent_ids filter' do
context 'when filtering by more than 100 parent ids' do
let(:too_many_parent_ids) { (1..101).to_a }
it 'throws an error' do
response = batch_sync { resolve_items(parent_ids: too_many_parent_ids) }
expect(response).to be_a(GraphQL::ExecutionError)
expect(response.message).to eq('You can only provide up to 100 parent IDs at once.')
end
end
context 'when converting global ids to work item ids' do
let_it_be(:work_item1) { create(:work_item) }
let_it_be(:work_item2) { create(:work_item) }
let(:global_ids) { [work_item1.to_global_id, work_item2.to_global_id] }
let(:context) { { arg_style: :internal_prepared } }
it 'correctly processes global IDs and maps to work item model_ids' do
expect(GitlabSchema).to receive(:parse_gids)
.with(global_ids, expected_type: ::WorkItem)
.and_call_original
batch_sync { resolve_items({ parent_ids: global_ids.map(&:to_s) }, context) }
end
end
end
end
end
@ -214,7 +243,8 @@ RSpec.describe Resolvers::WorkItemsResolver, feature_category: :team_planning do
def resolve_items(args = {}, context = {})
context[:current_user] = current_user
arg_style = context[:arg_style] ||= :internal
resolve(described_class, obj: project, args: args, ctx: context, arg_style: :internal)
resolve(described_class, obj: project, args: args, ctx: context, arg_style: arg_style)
end
end

View File

@ -41,7 +41,8 @@ RSpec.describe GitlabSchema.types['CurrentUser'], feature_category: :user_profil
subscribed
types
updatedAfter
updatedBefore]
updatedBefore
parentIds]
is_expected.to have_graphql_arguments(expected_fields)
is_expected.to have_graphql_type(Types::WorkItemType.connection_type)

View File

@ -0,0 +1,107 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Types::Namespaces::UserLevelPermissions::GroupNamespaceUserLevelPermissionsType, feature_category: :shared do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
subject(:type) { described_class.resolve_type(group, {}) }
it_behaves_like 'expose all user permissions fields for the namespace'
describe "permission values" do
let_it_be(:non_member) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:owner) { create(:user) }
let_it_be(:group) do
create(
:group,
:private,
guests: [guest],
developers: [developer],
maintainers: [maintainer],
owners: [owner]
)
end
context "for can_admin_label permission" do
where(:user_role, :expected) do
:non_member | false
:guest | false
:developer | true
:maintainer | true
:owner | true
end
with_them do
let(:current_user) { send(user_role) }
it "returns the correct permission value" do
actual = resolve_field(:can_admin_label, group, current_user: current_user)
expect(actual).to eq(expected)
end
end
end
context "for can_create_projects permission" do
where(:user_role, :expected) do
:non_member | false
:guest | false
:developer | false
:maintainer | true
:owner | true
end
with_them do
let(:current_user) { send(user_role) }
it "returns the correct permission value" do
actual = resolve_field(:can_create_projects, group, current_user: current_user)
expect(actual).to eq(expected)
end
end
end
end
context "when group settings restrict permissions" do
let_it_be(:group) { create(:group, :private) }
let_it_be(:developer) { create(:user) }
let_it_be(:owner) { create(:user) }
before_all do
group.add_developer(developer)
group.add_owner(owner)
end
context "when create project is restricted" do
before do
allow_next_instance_of(Ability) do |instance|
allow(instance).to receive(:can?).with(:create_projects, group).and_return(false)
end
end
it "returns false" do
expect(resolve_field(:can_create_projects, group, current_user: user)).to be(false)
end
end
context "when label administration is restricted" do
before do
allow_next_instance_of(Ability) do |instance|
allow(instance).to receive(:can?).with(:admin_label, group).and_return(false)
end
end
it "returns false" do
expect(resolve_field(:can_admin_label, group, current_user: user)).to be(false)
end
end
end
end

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Types::Namespaces::UserLevelPermissions::ProjectNamespaceUserLevelPermissionsType, feature_category: :shared do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :private) }
let_it_be(:project_namespace) { project.project_namespace }
subject(:type) { described_class.resolve_type(project_namespace, {}) }
it_behaves_like 'expose all user permissions fields for the namespace'
describe "permission values" do
let_it_be(:non_member) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:owner) { create(:user) }
before_all do
project.add_guest(guest)
project.add_developer(developer)
project.add_maintainer(maintainer)
project.add_owner(owner)
end
# Test the implemented method that returns actual permission values
context "for can_admin_label permission" do
where(:user_role, :expected) do
:non_member | false
:guest | false
:developer | true
:maintainer | true
:owner | true
end
with_them do
let(:current_user) { send(user_role) }
it "returns the correct permission value" do
actual = resolve_field(:can_admin_label, project_namespace, current_user: current_user)
expect(actual).to eq(expected)
end
end
end
# Unified test for all non-implemented permissions that return nil
context "for non-implemented permissions" do
let(:current_user) { maintainer }
it "returns nil for can_create_projects" do
expect(resolve_field(:can_create_projects, project_namespace, current_user: current_user)).to be(false)
end
if Gitlab.ee?
it "returns nil for can_bulk_edit_epics" do
expect(resolve_field(:can_bulk_edit_epics, project_namespace, current_user: current_user)).to be(false)
end
it "returns nil for can_create_epic" do
expect(resolve_field(:can_create_epic, project_namespace, current_user: current_user)).to be(false)
end
end
end
end
context "when project settings restrict permissions" do
context "when label administration is restricted" do
before do
allow_next_instance_of(Ability) do |instance|
allow(instance).to receive(:can?).with(:admin_label, project).and_return(false)
end
end
it "returns false" do
expect(resolve_field(:can_admin_label, project_namespace, current_user: user)).to be(false)
end
end
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Types::Namespaces::UserLevelPermissions::UserNamespaceUserLevelPermissionsType, feature_category: :shared do
it_behaves_like 'expose all user permissions fields for the namespace'
end

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.describe Types::Namespaces::UserLevelPermissions, feature_category: :shared do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
where(:namespace_class, :namespace_type_name) do
::Group | ::Types::Namespaces::UserLevelPermissions::GroupNamespaceUserLevelPermissionsType
::Namespaces::ProjectNamespace | ::Types::Namespaces::UserLevelPermissions::ProjectNamespaceUserLevelPermissionsType
end
with_them do
describe ".resolve_type" do
it "knows the correct type for objects" do
namespace = namespace_class.new
expect(described_class.resolve_type(namespace, {}))
.to eq(namespace_type_name)
end
end
describe ".orphan_types" do
it "includes the type" do
expect(described_class.orphan_types).to include(namespace_type_name)
end
end
end
it "raises an error for an unknown type" do
namespace = build(:project)
expect { described_class.resolve_type(namespace, {}) }
.to raise_error("Unknown GraphQL type for namespace type #{namespace.class}")
end
it_behaves_like "expose all user permissions fields for the namespace"
end

View File

@ -27,7 +27,8 @@ RSpec.describe Ci::PipelineSchedulesHelper, feature_category: :continuous_integr
schedules_path: pipeline_schedules_path(project),
settings_link: project_settings_ci_cd_path(project),
timezone_data: timezones.to_json,
can_set_pipeline_variables: 'false'
can_set_pipeline_variables: 'false',
worker_cron_expression: pipeline_schedule.worker_cron_expression
})
end
end

View File

@ -489,4 +489,31 @@ RSpec.describe DeployToken, feature_category: :continuous_delivery do
it { is_expected.to match(/gldt-[A-Za-z0-9_-]{20}/) }
end
describe '.with_encrypted_tokens' do
let(:deploy_token_1) { create(:deploy_token) }
let(:deploy_token_2) { create(:deploy_token) }
let(:deploy_token_3) { create(:deploy_token) }
it 'returns deploy tokens matching the given token values' do
encrypted_token_values = [deploy_token_1.token_encrypted, deploy_token_3.token_encrypted]
result = described_class.with_encrypted_tokens(encrypted_token_values)
expect(result).to contain_exactly(deploy_token_1, deploy_token_3)
expect(result).not_to include(deploy_token_2)
end
it 'returns an empty relation when no tokens match' do
result = described_class.with_encrypted_tokens(['non-existent-token'])
expect(result).to be_empty
end
it 'returns an empty relation when given an empty array' do
result = described_class.with_encrypted_tokens([])
expect(result).to be_empty
end
end
end

View File

@ -102,6 +102,30 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
end
end
describe '.with_parent_ids' do
let_it_be(:parent_item) { create(:work_item, :epic, project: reusable_project) }
context 'when given valid parent IDs' do
let_it_be(:child_item) { create(:work_item, project: reusable_project) }
before do
create(:parent_link, work_item_parent: parent_item, work_item: child_item)
end
it 'returns the work items with the specified parent IDs' do
expect(described_class.with_work_item_parent_ids([parent_item.id])).to contain_exactly(child_item)
end
end
context 'when work item does not have parent link' do
let_it_be(:work_item_without_parent) { create(:work_item, project: reusable_project) }
it 'does not return the work item' do
expect(described_class.with_work_item_parent_ids([parent_item.id])).to be_empty
end
end
end
describe '#create_dates_source_from_current_dates' do
let_it_be(:start_date) { nil }
let_it_be(:due_date) { nil }

View File

@ -123,11 +123,25 @@ RSpec.describe API::Files, feature_category: :source_code_management do
expect(response).to have_gitlab_http_status(expected_status)
end
context 'when the user has agentic chat permission for the project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :access_duo_agentic_chat, project)
.and_return(true)
end
it 'is successful' do
file_action
expect(response).to have_gitlab_http_status(expected_status)
end
end
context 'when the user does not have duo_workflow permission for the project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :duo_workflow, project)
.and_return(false)
.and_return(false)
end
it 'returns a forbidden error' do

View File

@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe API::PersonalAccessTokens::SelfRotation, feature_category: :system_access do
let(:path) { '/personal_access_tokens/self/rotate' }
let(:token) { create(:personal_access_token, user: current_user) }
let(:expiry_date) { Date.today + 1.week }
let(:expiry_date) { 1.week.from_now }
let(:params) { {} }
let_it_be(:current_user) { create(:user) }
@ -19,7 +19,7 @@ RSpec.describe API::PersonalAccessTokens::SelfRotation, feature_category: :syste
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['token']).not_to eq(token.token)
expect(json_response['expires_at']).to eq(expiry_date.to_s)
expect(json_response['expires_at']).to eq(expiry_date.to_date.iso8601)
expect(token.reload).to be_revoked
end
end
@ -38,7 +38,7 @@ RSpec.describe API::PersonalAccessTokens::SelfRotation, feature_category: :syste
it_behaves_like 'rotating token succeeds'
context 'when expiry is defined' do
let(:expiry_date) { Date.today + 1.month }
let(:expiry_date) { 1.month.from_now }
let(:params) { { expires_at: expiry_date } }
it_behaves_like 'rotating token succeeds'
@ -70,7 +70,7 @@ RSpec.describe API::PersonalAccessTokens::SelfRotation, feature_category: :syste
it_behaves_like 'rotating token succeeds'
context 'when expiry is defined' do
let(:expiry_date) { Date.today + 1.month }
let(:expiry_date) { 1.month.from_now }
let(:params) { { expires_at: expiry_date } }
it_behaves_like 'rotating token succeeds'
@ -182,7 +182,7 @@ RSpec.describe API::PersonalAccessTokens::SelfRotation, feature_category: :syste
it_behaves_like 'rotating token succeeds'
context 'when expiry is defined' do
let(:expiry_date) { Date.today + 1.month }
let(:expiry_date) { 1.month.from_now }
let(:params) { { expires_at: expiry_date } }
it_behaves_like 'rotating token succeeds'

View File

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe API::ResourceAccessTokens::SelfRotation, feature_category: :system_access do
let(:token) { create(:personal_access_token, user: current_user) }
let(:expiry_date) { Time.zone.today + 1.week }
let(:expiry_date) { 1.week.from_now }
let(:params) { {} }
subject(:rotate_token) { post(api(path, personal_access_token: token), params: params) }
@ -15,7 +15,7 @@ RSpec.describe API::ResourceAccessTokens::SelfRotation, feature_category: :syste
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['token']).not_to eq(token.token)
expect(json_response['expires_at']).to eq(expiry_date.to_s)
expect(json_response['expires_at']).to eq(expiry_date.to_date.iso8601)
expect(token.reload).to be_revoked
end
end
@ -123,7 +123,7 @@ RSpec.describe API::ResourceAccessTokens::SelfRotation, feature_category: :syste
it_behaves_like 'rotating token succeeds'
context 'when expiry is defined' do
let(:expiry_date) { Time.zone.today + 1.month }
let(:expiry_date) { 1.week.from_now }
let(:params) { { expires_at: expiry_date } }
it_behaves_like 'rotating token succeeds'

View File

@ -637,7 +637,7 @@ RSpec.describe API::ResourceAccessTokens, feature_category: :system_access do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['token']).not_to eq(token.token)
expect(json_response['expires_at']).to eq((Date.today + 1.week).to_s)
expect(json_response['expires_at']).to eq(1.week.from_now.to_date.iso8601)
end
end
@ -676,7 +676,7 @@ RSpec.describe API::ResourceAccessTokens, feature_category: :system_access do
if source_type == 'project'
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['token']).not_to eq(token.token)
expect(json_response['expires_at']).to eq((Date.today + 1.week).to_s)
expect(json_response['expires_at']).to eq(1.week.from_now.to_date.iso8601)
else
expect(response).to have_gitlab_http_status(:unauthorized)
end
@ -685,7 +685,7 @@ RSpec.describe API::ResourceAccessTokens, feature_category: :system_access do
end
context 'when expiry is defined' do
let(:expiry_date) { Date.today + 1.month }
let(:expiry_date) { 1.month.from_now }
let(:params) { { expires_at: expiry_date } }
before do
@ -698,7 +698,7 @@ RSpec.describe API::ResourceAccessTokens, feature_category: :system_access do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['token']).not_to eq(token.token)
expect(json_response['expires_at']).to eq(expiry_date.to_s)
expect(json_response['expires_at']).to eq(expiry_date.to_date.iso8601)
end
end

View File

@ -5007,7 +5007,7 @@ RSpec.describe API::Users, :with_current_organization, :aggregate_failures, feat
describe 'POST /users/:user_id/personal_access_tokens', :with_current_organization do
let(:name) { 'new pat' }
let(:description) { 'new pat description' }
let(:expires_at) { 3.days.from_now.to_date.to_s }
let(:expires_at) { 3.days.from_now }
let(:scopes) { %w[api read_user] }
let(:path) { "/users/#{user.id}/personal_access_tokens" }
let(:params) { { name: name, scopes: scopes, expires_at: expires_at, description: description } }
@ -5049,7 +5049,7 @@ RSpec.describe API::Users, :with_current_organization, :aggregate_failures, feat
expect(json_response['name']).to eq(name)
expect(json_response['description']).to eq(description)
expect(json_response['scopes']).to eq(scopes)
expect(json_response['expires_at']).to eq(expires_at)
expect(json_response['expires_at']).to eq(expires_at.to_date.iso8601)
expect(json_response['id']).to be_present
expect(json_response['created_at']).to be_present
expect(json_response['active']).to be_truthy

View File

@ -53,7 +53,7 @@ RSpec.describe Groups::Settings::AccessTokensController, feature_category: :syst
end
describe 'POST /:namespace/-/settings/access_tokens' do
let(:access_token_params) { { name: 'Nerd bot', description: 'Nerd bot description', scopes: ["api"], expires_at: Date.today + 1.month } }
let(:access_token_params) { { name: 'Nerd bot', description: 'Nerd bot description', scopes: ["api"], expires_at: 1.month.from_now } }
subject do
post group_settings_access_tokens_path(resource), params: { resource_access_token: access_token_params }
@ -84,7 +84,7 @@ RSpec.describe Groups::Settings::AccessTokensController, feature_category: :syst
end
context 'with custom access level' do
let(:access_token_params) { { name: 'Nerd bot', scopes: ["api"], expires_at: Date.today + 1.month, access_level: 20 } }
let(:access_token_params) { { name: 'Nerd bot', scopes: ["api"], expires_at: 1.month.from_now, access_level: 20 } }
subject { post group_settings_access_tokens_path(resource), params: { resource_access_token: access_token_params } }

View File

@ -54,7 +54,7 @@ RSpec.describe Projects::Settings::AccessTokensController, feature_category: :sy
end
describe 'POST /:namespace/:project/-/settings/access_tokens' do
let(:access_token_params) { { name: 'Nerd bot', description: 'Nerd bot description', scopes: ["api"], expires_at: Date.today + 1.month } }
let(:access_token_params) { { name: 'Nerd bot', description: 'Nerd bot description', scopes: ["api"], expires_at: 1.month.from_now } }
subject do
post project_settings_access_tokens_path(resource), params: { resource_access_token: access_token_params }
@ -85,7 +85,7 @@ RSpec.describe Projects::Settings::AccessTokensController, feature_category: :sy
end
context 'with custom access level' do
let(:access_token_params) { { name: 'Nerd bot', scopes: ["api"], expires_at: Date.today + 1.month, access_level: 20 } }
let(:access_token_params) { { name: 'Nerd bot', scopes: ["api"], expires_at: 1.month.from_now, access_level: 20 } }
subject { post project_settings_access_tokens_path(resource), params: { resource_access_token: access_token_params } }

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
require "spec_helper"
RSpec.shared_examples "expose all user permissions fields for the namespace" do
include GraphqlHelpers
specify do
expected_fields = %i[
canAdminLabel
canCreateProjects
]
if Gitlab.ee?
expected_fields.push(*%i[
canCreateEpic
canBulkEditEpics
])
end
expect(described_class).to have_graphql_fields(*expected_fields)
end
end

View File

@ -168,7 +168,7 @@ RSpec.shared_examples 'POST resource access tokens available' do
expect(created_token.name).to eq(access_token_params[:name])
expect(created_token.description).to eq(access_token_params[:description])
expect(created_token.scopes).to eq(access_token_params[:scopes])
expect(created_token.expires_at).to eq(access_token_params[:expires_at])
expect(created_token.expires_at).to eq(access_token_params[:expires_at].to_date)
expect(resource.member(created_token.user).access_level).to eq(access_level)
end