Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-03-26 18:09:43 +00:00
parent 725adcc1b7
commit bb27269b9b
93 changed files with 712 additions and 670 deletions

View File

@ -46,6 +46,11 @@ export default {
required: false,
default: null,
},
initialProjectKeys: {
type: String,
required: false,
default: null,
},
isValidated: {
type: Boolean,
required: false,
@ -56,7 +61,7 @@ export default {
return {
enableJiraIssues: this.initialEnableJiraIssues,
projectKey: this.initialProjectKey,
projectKeys: null,
projectKeys: this.initialProjectKeys,
};
},
computed: {

View File

@ -22,6 +22,7 @@ function parseDatasetToProps(data) {
projectId,
commentDetail,
projectKey,
projectKeys,
learnMorePath,
aboutPricingUrl,
triggerEvents,
@ -88,6 +89,7 @@ function parseDatasetToProps(data) {
initialEnableJiraVulnerabilities: enableJiraVulnerabilities,
initialVulnerabilitiesIssuetype: vulnerabilitiesIssuetype,
initialProjectKey: projectKey,
initialProjectKeys: projectKeys,
},
googleCloudArtifactRegistryProps: {
artifactRegistryPath,

View File

@ -1,4 +1,5 @@
<script>
import { isEmpty } from 'lodash';
import { GlTableLite, GlLink, GlEmptyState, GlButton } from '@gitlab/ui';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import RegistrySearch from '~/vue_shared/components/registry/registry_search.vue';
@ -129,6 +130,9 @@ export default {
hasItems() {
return this.candidates.length > 0;
},
hasMetadata() {
return !isEmpty(this.experiment.metadata);
},
deleteButtonInfo() {
return {
deletePath: this.experiment.path,
@ -176,76 +180,94 @@ export default {
}}</gl-button>
<delete-button v-bind="deleteButtonInfo" />
</model-experiments-header>
<section>
<registry-search
:filters="filters"
:sorting="sorting"
:sortable-fields="sortableFields"
@sorting:changed="updateSortingAndEmitUpdate"
@filter:changed="updateFilters"
@filter:submit="submitFilters"
@filter:clear="filters = []"
/>
<registry-search
:filters="filters"
:sorting="sorting"
:sortable-fields="sortableFields"
@sorting:changed="updateSortingAndEmitUpdate"
@filter:changed="updateFilters"
@filter:submit="submitFilters"
@filter:clear="filters = []"
/>
<div v-if="hasItems" class="gl-overflow-x-auto">
<gl-table-lite
:fields="fields"
:items="tableItems"
show-empty
small
class="gl-mt-0! ml-candidate-table"
>
<template #cell()="data">
<div>{{ data.value }}</div>
</template>
<div v-if="hasItems" class="gl-overflow-x-auto">
<gl-table-lite
:fields="fields"
:items="tableItems"
show-empty
small
class="gl-mt-0! ml-candidate-table"
>
<template #cell()="data">
<div>{{ data.value }}</div>
</template>
<template #cell(nameColumn)="data">
<gl-link :href="data.value.details_path">
<span v-if="data.value.name"> {{ data.value.name }}</span>
<span v-else class="gl-font-style-italic">{{ $options.i18n.NO_CANDIDATE_NAME }}</span>
</gl-link>
</template>
<template #cell(nameColumn)="data">
<gl-link :href="data.value.details_path">
<span v-if="data.value.name"> {{ data.value.name }}</span>
<span v-else class="gl-font-style-italic">{{ $options.i18n.NO_CANDIDATE_NAME }}</span>
</gl-link>
</template>
<template #cell(artifact)="data">
<gl-link v-if="data.value" :href="data.value" target="_blank">{{
$options.i18n.ARTIFACTS_LABEL
}}</gl-link>
<div v-else class="gl-font-style-italic gl-text-gray-500">
{{ $options.i18n.NO_ARTIFACT }}
</div>
</template>
<template #cell(artifact)="data">
<gl-link v-if="data.value" :href="data.value" target="_blank">{{
$options.i18n.ARTIFACTS_LABEL
}}</gl-link>
<div v-else class="gl-font-style-italic gl-text-gray-500">
{{ $options.i18n.NO_ARTIFACT }}
</div>
</template>
<template #cell(created_at)="data">
<time-ago :time="data.value" />
</template>
<template #cell(created_at)="data">
<time-ago :time="data.value" />
</template>
<template #cell(user)="data">
<gl-link v-if="data.value" :href="data.value.path">@{{ data.value.username }}</gl-link>
<div v-else>{{ $options.i18n.NO_DATA_CONTENT }}</div>
</template>
<template #cell(user)="data">
<gl-link v-if="data.value" :href="data.value.path">@{{ data.value.username }}</gl-link>
<div v-else>{{ $options.i18n.NO_DATA_CONTENT }}</div>
</template>
<template #cell(ci_job)="data">
<gl-link v-if="data.value" :href="data.value.path" target="_blank">{{
data.value.name
}}</gl-link>
<div v-else class="gl-font-style-italic gl-text-gray-500">
{{ $options.i18n.NO_JOB }}
</div>
</template>
</gl-table-lite>
</div>
<template #cell(ci_job)="data">
<gl-link v-if="data.value" :href="data.value.path" target="_blank">{{
data.value.name
}}</gl-link>
<div v-else class="gl-font-style-italic gl-text-gray-500">
{{ $options.i18n.NO_JOB }}
</div>
</template>
</gl-table-lite>
</div>
<gl-empty-state
v-else
:title="$options.i18n.EMPTY_STATE_TITLE_LABEL"
:primary-button-text="$options.i18n.CREATE_NEW_LABEL"
:primary-button-link="$options.constants.CREATE_CANDIDATE_HELP_PATH"
:svg-path="emptyStateSvgPath"
:svg-height="null"
:description="$options.i18n.EMPTY_STATE_DESCRIPTION_LABEL"
class="gl-py-8"
/>
<gl-empty-state
v-else
:title="$options.i18n.EMPTY_STATE_TITLE_LABEL"
:primary-button-text="$options.i18n.CREATE_NEW_LABEL"
:primary-button-link="$options.constants.CREATE_CANDIDATE_HELP_PATH"
:svg-path="emptyStateSvgPath"
:svg-height="null"
:description="$options.i18n.EMPTY_STATE_DESCRIPTION_LABEL"
class="gl-py-8"
/>
<keyset-pagination v-if="displayPagination" v-bind="pageInfo" />
</section>
<keyset-pagination v-if="displayPagination" v-bind="pageInfo" />
<section>
<div class="experiment-metadata">
<h3 :class="$options.HEADER_CLASSES">{{ $options.i18n.METADATA_LABEL }}</h3>
<table v-if="hasMetadata">
<tbody>
<tr v-for="item in experiment.metadata" :key="item.name">
<td class="gl-font-weight-bold">{{ item.name }}</td>
<td>{{ item.value }}</td>
</tr>
</tbody>
</table>
<div v-else class="gl-text-secondary">{{ $options.i18n.NO_METADATA_MESSAGE }}</div>
</div>
</section>
</div>
</template>

View File

@ -22,3 +22,5 @@ export const DELETE_EXPERIMENT_CONFIRMATION_MESSAGE = s__(
export const DELETE_EXPERIMENT_PRIMARY_ACTION_LABEL = s__('MlExperimentTracking|Delete experiment');
export const DELETE_EXPERIMENT_MODAL_TITLE = s__('MLExperimentTracking|Delete experiment?');
export const DOWNLOAD_AS_CSV_LABEL = s__('MlExperimentTracking|Download as CSV');
export const METADATA_LABEL = s__('MlExperimentTracking|Experiment metadata');
export const NO_METADATA_MESSAGE = s__('MlExperimentTracking|No logged experiment metadata');

View File

@ -25,6 +25,10 @@ export function expandSection(sectionArg) {
.addClass('animating')
.one('animationend.animateSection', () => $section.removeClass('animating'));
}
InternalEvents.trackEvent('click_expand_panel_on_settings', undefined, {
label: $section.find('.settings-title').text(),
});
}
export function closeSection(sectionArg) {

View File

@ -65,7 +65,7 @@ export default {
v-else
:users="users"
:issuable-type="issuableType"
class="gl-text-gray-800 hide-collapsed"
class="gl-text-gray-800 hide-collapsed gl-pt-2"
/>
</div>
</template>

View File

@ -110,10 +110,13 @@ export default {
><gl-icon name="question-o" class="gl-text-blue-600"
/></gl-link>
</div>
<div class="title hide-collapsed gl-mb-2 gl-line-height-20 gl-font-weight-bold">
<div class="hide-collapsed gl-line-height-20 gl-font-weight-bold">
{{ contactsLabel }}
</div>
<div class="hide-collapsed gl-display-flex gl-flex-wrap">
<div
class="hide-collapsed gl-display-flex gl-flex-wrap"
:class="contacts.length > 0 ? 'gl-mt-2' : ''"
>
<div
v-for="(contact, index) in contacts"
:id="`contact_container_${index}`"

View File

@ -406,6 +406,7 @@ export default {
:supports-lock-on-merge="isLockOnMergeSupported"
:labels-filter-base-path="labelsFilterBasePath"
:labels-filter-param="labelsFilterParam"
class="gl-pt-2"
@onLabelRemove="handleLabelRemove"
@onCollapsedValueClick="handleCollapsedValueClick"
>

View File

@ -91,7 +91,10 @@ export default {
<template>
<div>
<div v-if="showParticipantLabel" class="title gl-line-height-20 gl-font-weight-bold gl-mb-2">
<div
v-if="showParticipantLabel"
class="gl-display-flex gl-align-items-center gl-line-height-24 gl-text-gray-900 gl-font-weight-bold gl-mb-2"
>
<gl-loading-icon v-if="loading" inline />
{{ participantLabel }}
</div>

View File

@ -33,7 +33,9 @@ export default {
};
</script>
<template>
<div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold">
<div
class="hide-collapsed gl-display-flex gl-align-items-center gl-line-height-20 gl-text-gray-900 gl-font-weight-bold"
>
{{ reviewerTitle }}
<gl-loading-icon v-if="loading" size="sm" inline class="align-bottom" />
<a

View File

@ -183,6 +183,7 @@ export default {
:users="reviewers"
:editable="canUpdate"
:issuable-type="issuableType"
class="gl-pt-2"
@request-review="requestReview"
@assign-self="reviewBySelf"
/>

View File

@ -129,11 +129,14 @@ export default {
],
};
}
return REVIEW_STATE_ICONS[user.mergeRequestInteraction.reviewState];
return (
REVIEW_STATE_ICONS[user.mergeRequestInteraction.reviewState] ||
REVIEW_STATE_ICONS.UNREVIEWED
);
},
showRequestReviewButton(user) {
if (!user.mergeRequestInteraction.approved) {
return user.mergeRequestInteraction.reviewState !== 'UNREVIEWED';
return !['UNREVIEWED', 'UNAPPROVED'].includes(user.mergeRequestInteraction.reviewState);
}
return true;

View File

@ -6,8 +6,9 @@ import {
GlModal,
GlAlert,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
GlDisclosureDropdown,
GlDisclosureDropdownGroup,
GlDisclosureDropdownItem,
GlButton,
GlTooltipDirective,
} from '@gitlab/ui';
@ -29,6 +30,7 @@ export const i18n = {
{ spammable_titlecase: __('Snippet') },
),
snippetSpamFailure: s__('Snippets|Error with Akismet. Please check the logs for more info.'),
snippetAction: s__('Snippets|Snippet actions'),
};
export default {
@ -39,8 +41,9 @@ export default {
GlModal,
GlAlert,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
GlDisclosureDropdown,
GlDisclosureDropdownGroup,
GlDisclosureDropdownItem,
TimeAgoTooltip,
GlButton,
},
@ -79,6 +82,7 @@ export default {
errorMessage: '',
canCreateSnippet: false,
isDeleteModalVisible: false,
isDropdownShown: false,
};
},
computed: {
@ -90,44 +94,52 @@ export default {
? __('Authored %{timeago} by %{author}')
: __('Authored %{timeago}');
},
personalSnippetActions() {
return [
{
condition: this.snippet.userPermissions.updateSnippet,
text: __('Edit'),
href: this.editLink,
disabled: this.snippetHasBinary,
title: this.snippetHasBinary
? __('Snippets with non-text files can only be edited via Git.')
: undefined,
editItem() {
return {
text: __('Edit'),
href: this.editLink,
disabled: this.snippetHasBinary,
title: this.snippetHasBinary
? __('Snippets with non-text files can only be edited via Git.')
: undefined,
extraAttrs: {
class: 'gl-sm-display-none!',
},
{
condition: this.snippet.userPermissions.adminSnippet,
text: __('Delete'),
click: this.showDeleteModal,
variant: 'danger',
category: 'secondary',
};
},
canReportSpaCheck() {
return this.canReportSpam && !isEmpty(this.reportAbusePath);
},
spamItem() {
return {
text: __('Submit as spam'),
action: () => this.submitAsSpam(),
};
},
deleteItem() {
return {
text: __('Delete'),
action: () => this.showDeleteModal(),
extraAttrs: {
class: 'gl-text-red-500!',
},
{
condition: this.canCreateSnippet,
text: __('New snippet'),
href: this.snippet.project
? joinPaths(this.snippet.project.webUrl, '-/snippets/new')
: joinPaths('/', gon.relative_url_root, '/-/snippets/new'),
variant: 'confirm',
category: 'secondary',
},
{
condition: this.canReportSpam && !isEmpty(this.reportAbusePath),
text: __('Submit as spam'),
click: this.submitAsSpam,
title: __('Submit as spam'),
loading: this.isSubmittingSpam,
},
];
};
},
newSnippetItem() {
return {
text: __('New snippet'),
href: this.snippet.project
? joinPaths(this.snippet.project.webUrl, '-/snippets/new')
: joinPaths('/', gon.relative_url_root, '/-/snippets/new'),
};
},
hasPersonalSnippetActions() {
return Boolean(this.personalSnippetActions.filter(({ condition }) => condition).length);
return (
this.snippet.userPermissions.updateSnippet ||
this.canCreateSnippet ||
this.snippet.userPermissions.adminSnippet ||
this.canReportSpaCheck
);
},
editLink() {
return `${this.snippet.webUrl}/edit`;
@ -157,6 +169,9 @@ export default {
return 'earth';
}
},
showDropdownTooltip() {
return !this.isDropdownShown ? this.$options.i18n.snippetAction : '';
},
},
methods: {
redirectToSnippets() {
@ -170,6 +185,12 @@ export default {
showDeleteModal() {
this.isDeleteModalVisible = true;
},
onShowDropdown() {
this.isDropdownShown = true;
},
onHideDropdown() {
this.isDropdownShown = false;
},
deleteSnippet() {
this.isLoading = true;
this.$apollo
@ -257,47 +278,62 @@ export default {
</div>
</div>
<div v-if="hasPersonalSnippetActions" class="detail-page-header-actions gl-align-self-start">
<div class="d-none d-sm-flex">
<template v-for="(action, index) in personalSnippetActions">
<div
v-if="action.condition"
:key="index"
v-gl-tooltip
:title="action.title"
class="d-inline-block"
:class="{ 'gl-ml-3': index > 0 }"
>
<div
v-if="hasPersonalSnippetActions"
class="detail-page-header-actions gl-display-flex gl-align-self-center gl-gap-3 gl-relative"
>
<gl-button
v-if="snippet.userPermissions.updateSnippet"
:href="editItem.href"
:title="editItem.title"
:disabled="editItem.disabled"
class="gl-display-none gl-sm-display-inline-block"
data-testid="snippet-action-button"
:data-qa-action="editItem.text"
>
{{ editItem.text }}
</gl-button>
<gl-disclosure-dropdown
data-testid="snippets-more-actions-dropdown"
@shown="onShowDropdown"
@hidden="onHideDropdown"
>
<template #toggle>
<div class="gl-w-full gl-min-h-7">
<gl-button
:disabled="action.disabled"
:loading="action.loading"
:variant="action.variant"
:category="action.category"
:class="action.cssClass"
:href="action.href"
data-testid="snippet-action-button"
:data-qa-action="action.text"
@click="action.click ? action.click() : undefined"
>{{ action.text }}</gl-button
class="gl-sm-display-none! gl-new-dropdown-toggle gl-absolute gl-top-0 gl-left-0 gl-w-full"
button-text-classes="gl-display-flex gl-justify-content-space-between gl-w-full"
category="secondary"
tabindex="0"
>
<span>{{ $options.i18n.snippetAction }}</span>
<gl-icon class="dropdown-chevron" name="chevron-down" />
</gl-button>
<gl-button
v-gl-tooltip="showDropdownTooltip"
class="gl-display-none gl-sm-display-flex! gl-new-dropdown-toggle gl-new-dropdown-icon-only gl-new-dropdown-toggle-no-caret"
category="tertiary"
icon="ellipsis_v"
:aria-label="$options.i18n.snippetAction"
tabindex="0"
data-testid="snippets-more-actions-dropdown-toggle"
/>
</div>
</template>
</div>
<div class="d-block d-sm-none dropdown">
<gl-dropdown :text="__('Options')" block>
<template v-for="(action, index) in personalSnippetActions">
<gl-dropdown-item
v-if="action.condition"
:key="index"
:disabled="action.disabled"
:title="action.title"
:href="action.href"
@click="action.click ? action.click() : undefined"
>{{ action.text }}</gl-dropdown-item
>
</template>
</gl-dropdown>
</div>
<gl-disclosure-dropdown-item
v-if="snippet.userPermissions.updateSnippet"
:item="editItem"
/>
<gl-disclosure-dropdown-item v-if="canCreateSnippet" :item="newSnippetItem" />
<gl-disclosure-dropdown-group bordered>
<gl-disclosure-dropdown-item v-if="canReportSpaCheck" :item="spamItem" />
<gl-disclosure-dropdown-item
v-if="snippet.userPermissions.adminSnippet"
:item="deleteItem"
/>
</gl-disclosure-dropdown-group>
</gl-disclosure-dropdown>
</div>
<gl-modal

View File

@ -37,11 +37,6 @@
.detail-page-header-actions {
flex: 0 0 auto;
@include media-breakpoint-down(sm) {
width: 100%;
margin-top: 10px;
}
}
.detail-page-header-meta {

View File

@ -13,6 +13,7 @@ table.ml-candidate-table {
}
}
.experiment-metadata table,
table.candidate-details {
td {
padding: $gl-spacing-scale-3 $gl-spacing-scale-3 $gl-spacing-scale-3 0;

View File

@ -75,7 +75,6 @@ module Integrations
:password,
:priority,
:project_key,
:project_keys,
:project_name,
:project_url,
:recipients,

View File

@ -100,18 +100,23 @@ class SearchController < ApplicationController
scope = search_service.scope
@search_level = search_service.level
@search_type = search_type
count = 0
ApplicationRecord.with_fast_read_statement_timeout do
count = search_service.search_results.formatted_count(scope)
@global_search_duration_s = Benchmark.realtime do
ApplicationRecord.with_fast_read_statement_timeout do
count = search_service.search_results.formatted_count(scope)
end
# Users switching tabs will keep fetching the same tab counts so it's a
# good idea to cache in their browser just for a short time. They can still
# clear cache if they are seeing an incorrect count but inaccurate count is
# not such a bad thing.
expires_in 1.minute
render json: { count: count }
end
# Users switching tabs will keep fetching the same tab counts so it's a
# good idea to cache in their browser just for a short time. They can still
# clear cache if they are seeing an incorrect count but inaccurate count is
# not such a bad thing.
expires_in 1.minute
render json: { count: count }
end
def autocomplete

View File

@ -11,5 +11,9 @@ module Types
description: 'Merge request reviewer has reviewed.'
value 'REQUESTED_CHANGES', value: 'requested_changes',
description: 'Merge request reviewer has requested changes.'
value 'APPROVED', value: 'approved',
description: 'Merge request reviewer has approved the changes.'
value 'UNAPPROVED', value: 'unapproved',
description: 'Merge request reviewer removed their approval of the changes.'
end
end

View File

@ -57,3 +57,5 @@ module Types
end
end
end
Types::MergeRequests::DetailedMergeStatusEnum.prepend_mod_with('Types::MergeRequests::DetailedMergeStatusEnum')

View File

@ -8,6 +8,7 @@ module Projects
def experiment_as_data(experiment)
data = {
name: experiment.name,
metadata: experiment.metadata,
path: link_to_experiment(experiment.project, experiment)
}

View File

@ -7,7 +7,9 @@ module MergeRequestReviewerState
enum state: {
unreviewed: 0,
reviewed: 1,
requested_changes: 2
requested_changes: 2,
approved: 3,
unapproved: 4
}
validates :state,

View File

@ -61,7 +61,7 @@ module Integrations
before_save :copy_project_key_to_project_keys,
if: -> {
Feature.disabled?(:jira_multiple_project_keys, project&.group)
Feature.disabled?(:jira_multiple_project_keys, group || project&.group)
}
after_commit :update_deployment_type, on: [:create, :update], if: :update_deployment_type?
@ -417,6 +417,10 @@ module Integrations
group_level? || project_level?
end
def project_keys_as_string
project_keys.join(',')
end
private
def jira_issue_match_regex

View File

@ -2139,6 +2139,14 @@ class MergeRequest < ApplicationRecord
merge_request_reviewers.where(user_id: user_ids)
end
def has_changes_requested?
merge_request_reviewers.exists?(state: :requested_changes)
end
def batch_update_reviewer_state(user_ids, state)
merge_request_reviewers.where(user_id: user_ids).update_all(state: state)
end
def enabled_reports
{
sast: report_type_enabled?(:sast),

View File

@ -13,12 +13,13 @@ module MergeRequests
return success unless save_approval(approval)
update_reviewer_state(merge_request, current_user, :approved)
reset_approvals_cache(merge_request)
merge_request_activity_counter.track_approve_mr_action(user: current_user, merge_request: merge_request)
trigger_merge_request_merge_status_updated(merge_request)
trigger_merge_request_reviewers_updated(merge_request)
trigger_merge_request_approval_state_updated(merge_request)
# Approval side effects (things not required to be done immediately but

View File

@ -282,6 +282,12 @@ module MergeRequests
MergeRequests::RemoveApprovalService.new(project: project, current_user: current_user)
.execute(merge_request)
end
def update_reviewer_state(merge_request, user, state)
::MergeRequests::UpdateReviewerStateService
.new(project: merge_request.project, current_user: user)
.execute(merge_request, state)
end
end
end

View File

@ -15,11 +15,11 @@ module MergeRequests
trigger_approval_hooks(merge_request) do
next unless approval.destroy_all # rubocop: disable Cop/DestroyAll
update_reviewer_state(merge_request, current_user, :unapproved)
reset_approvals_cache(merge_request)
create_note(merge_request)
merge_request_activity_counter.track_unapprove_mr_action(user: current_user)
trigger_merge_request_merge_status_updated(merge_request)
trigger_merge_request_reviewers_updated(merge_request)
trigger_merge_request_approval_state_updated(merge_request)
end

View File

@ -12,7 +12,7 @@
= s_('SnippetsEmptyState|Store, share, and embed small pieces of code and text.')
.gl-mt-3<
- if button_path
= link_button_to s_('SnippetsEmptyState|New snippet'), button_path, title: s_('SnippetsEmptyState|New snippet'), id: 'new_snippet_link', data: { testid: 'create-first-snippet-link' }, variant: :confirm
= link_button_to s_('SnippetsEmptyState|New snippet'), button_path, title: s_('SnippetsEmptyState|New snippet'), data: { testid: 'create-first-snippet-link' }, variant: :confirm
= link_button_to s_('SnippetsEmptyState|Documentation'), help_page_path('user/snippets'), title: s_('SnippetsEmptyState|Documentation')
- else
%h4.gl-text-center= s_('SnippetsEmptyState|There are no snippets to show.')

View File

@ -0,0 +1,20 @@
---
description: User expands a panel on a settings page
internal_events: true
action: click_expand_panel_on_settings
identifiers:
- project
- namespace
- user
product_section: core_platform
product_stage: manage
product_group: foundations
milestone: '16.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/451458
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_distinct_user_id_from_click_expand_panel_on_settings_monthly
description: Monthly count of unique users who expanded a settings panel
product_section: core_platform
product_stage: manage
product_group: foundations
performance_indicator_type: []
value_type: number
status: active
milestone: '16.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/451458
time_frame: 28d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: click_expand_panel_on_settings
unique: user.id

View File

@ -0,0 +1,24 @@
---
key_path: redis_hll_counters.count_distinct_user_id_from_click_expand_panel_on_settings_weekly
description: Weekly count of unique users who expanded a settings panel
product_section: core_platform
product_stage: manage
product_group: foundations
performance_indicator_type: []
value_type: number
status: active
milestone: '16.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/451458
time_frame: 7d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: click_expand_panel_on_settings
unique: user.id

View File

@ -7,4 +7,12 @@ feature_categories:
description: Model to store public keys used by Sentry SDK for Error Tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66466
milestone: '14.2'
gitlab_schema: gitlab_main
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide
allow_cross_transactions:
- gitlab_main_clusterwide
allow_cross_foreign_keys:
- gitlab_main_clusterwide
sharding_key:
project_id: projects

View File

@ -7,4 +7,12 @@ feature_categories:
description: Persists error data for the Error Tracking's GitLab backend
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64712
milestone: '14.1'
gitlab_schema: gitlab_main
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide
allow_cross_transactions:
- gitlab_main_clusterwide
allow_cross_foreign_keys:
- gitlab_main_clusterwide
sharding_key:
project_id: projects

View File

@ -7,4 +7,12 @@ feature_categories:
description: Project settings related to Error Tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24047
milestone: '11.7'
gitlab_schema: gitlab_main
gitlab_schema: gitlab_main_cell
allow_cross_joins:
- gitlab_main_clusterwide
allow_cross_transactions:
- gitlab_main_clusterwide
allow_cross_foreign_keys:
- gitlab_main_clusterwide
sharding_key:
project_id: projects

View File

@ -1,11 +0,0 @@
---
redirect_to: '../audit_event_schema.md'
remove_date: '2024-03-21'
---
This document was moved to [another location](../audit_event_schema.md).
<!-- This redirect file can be deleted after <2024-03-21>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,11 +0,0 @@
---
redirect_to: 'audit_event_reports.md'
remove_date: '2024-03-21'
---
This document was moved to [another location](audit_event_reports.md).
<!-- This redirect file can be deleted after <2024-03-21>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,11 +0,0 @@
---
redirect_to: 'audit_event_reports.md'
remove_date: '2024-03-21'
---
This document was moved to [another location](audit_event_reports.md).
<!-- This redirect file can be deleted after <2024-03-21>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -60,8 +60,8 @@ These features can help provide visibility into GitLab and audit what is happeni
| Feature | Instances | Groups | Projects | Description |
|:-------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Audit events](audit_events.md) | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | To maintain the integrity of your code, audit events give administrators the ability to view any modifications made in the GitLab server in an advanced audit events system, so you can control, analyze, and track every change. |
| [Audit reports](audit_reports.md) | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Create and access reports based on the audit events that have occurred. Use pre-built GitLab reports or the API to build your own. |
| [Audit events](audit_event_reports.md) | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | To maintain the integrity of your code, audit events give administrators the ability to view any modifications made in the GitLab server in an advanced audit events system, so you can control, analyze, and track every change. |
| [Audit reports](audit_event_reports.md) | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Create and access reports based on the audit events that have occurred. Use pre-built GitLab reports or the API to build your own. |
| [Auditor users](auditor_users.md) | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Auditor users are users who are given read-only access to all projects, groups, and other resources on the GitLab instance. |
| [Compliance center](../user/compliance/compliance_center/index.md) | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Quickly get visibility into the compliance posture of your organization through compliance standards adherence reporting and violations reports. Manage your groups compliance frameworks centrally. |

View File

@ -11,7 +11,7 @@ DETAILS:
**Offering:** Self-managed
GitLab has an advanced log system where everything is logged, so you can analyze your instance using various system log
files. The log system is similar to [audit events](../audit_events.md).
files. The log system is similar to [audit events](../audit_event_reports.md).
System log files are typically plain text in a standard log file format.
This guide talks about how to read and use these system log files.

View File

@ -292,7 +292,7 @@ DETAILS:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24605) in GitLab 12.7.
To maintain integrity of user details in [Audit Events](../../administration/audit_events.md), GitLab administrators can choose to disable a user's ability to change their profile name.
To maintain integrity of user details in [Audit Events](../../administration/audit_event_reports.md), GitLab administrators can choose to disable a user's ability to change their profile name.
To do this:

View File

@ -78,8 +78,6 @@ To modify the maximum file size for exports in GitLab:
## Max import size
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MiB to unlimited in GitLab 13.8.
To modify the maximum file size for imports in GitLab:
1. On the left sidebar, at the bottom, select **Admin Area**.
@ -160,20 +158,23 @@ To modify the maximum decompressed file size for imports in GitLab:
## Maximum number of simultaneous import jobs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143875) in GitLab 16.10.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143875) in GitLab 16.11.
When you import a project using [GitHub Importer](../../user/project/import/github.md),
[Bitbucket Cloud Importer](../../user/project/import/bitbucket.md) or
[Bitbucket Server Importer](../../user/project/import/bitbucket_server.md), you
can specify the maximum number of import jobs that are executed simultaneously.
You can specify the maximum number of import jobs that are executed simultaneously for:
The job limit is not applied when importing merge requests because there is a hard-coded limit for merge requests to avoid overloading servers.
- [GitHub importer](../../user/project/import/github.md)
- [Bitbucket Cloud importer](../../user/project/import/bitbucket.md)
- [Bitbucket Server importer](../../user/project/import/bitbucket_server.md)
The job limit is not applied when importing merge requests because there is a hard-coded limit for merge requests to
avoid overloading servers.
The default job limit is:
- For the GitHub importer, 1000.
- For the Bitbucket Cloud and Bitbucket Server importer, 100. The Bitbucket importers have a low default limit because we haven't yet determined
a good default limit. Administrators of self-managed GitLab instances should experiment with a higher limit.
- For the Bitbucket Cloud and Bitbucket Server importer, 100. The Bitbucket importers have a low default limit because
we haven't yet determined a good default limit. Administrators of self-managed GitLab instances should experiment with
a higher limit.
To modify this setting:

View File

@ -31719,6 +31719,7 @@ Detailed representation of whether a GitLab merge request can be merged.
| <a id="detailedmergestatusnot_approved"></a>`NOT_APPROVED` | Merge request must be approved before merging. |
| <a id="detailedmergestatusnot_open"></a>`NOT_OPEN` | Merge request must be open before merging. |
| <a id="detailedmergestatuspreparing"></a>`PREPARING` | Merge request diff is being created. |
| <a id="detailedmergestatusrequested_changes"></a>`REQUESTED_CHANGES` | Indicates a reviewer has requested changes. |
| <a id="detailedmergestatusunchecked"></a>`UNCHECKED` | Merge status has not been checked. |
### `DiffPositionType`
@ -32333,8 +32334,10 @@ State of a review of a GitLab merge request.
| Value | Description |
| ----- | ----------- |
| <a id="mergerequestreviewstateapproved"></a>`APPROVED` | Merge request reviewer has approved the changes. |
| <a id="mergerequestreviewstaterequested_changes"></a>`REQUESTED_CHANGES` | Merge request reviewer has requested changes. |
| <a id="mergerequestreviewstatereviewed"></a>`REVIEWED` | Merge request reviewer has reviewed. |
| <a id="mergerequestreviewstateunapproved"></a>`UNAPPROVED` | Merge request reviewer removed their approval of the changes. |
| <a id="mergerequestreviewstateunreviewed"></a>`UNREVIEWED` | Awaiting review from merge request reviewer. |
### `MergeRequestSort`
@ -32413,6 +32416,7 @@ Representation of mergeability check identifier.
| <a id="mergeabilitycheckidentifierneed_rebase"></a>`NEED_REBASE` | Checks whether the merge request needs to be rebased. |
| <a id="mergeabilitycheckidentifiernot_approved"></a>`NOT_APPROVED` | Checks whether the merge request is approved. |
| <a id="mergeabilitycheckidentifiernot_open"></a>`NOT_OPEN` | Checks whether the merge request is open. |
| <a id="mergeabilitycheckidentifierrequested_changes"></a>`REQUESTED_CHANGES` | Checks whether the merge request has changes requested. |
| <a id="mergeabilitycheckidentifierstatus_checks_must_pass"></a>`STATUS_CHECKS_MUST_PASS` | Checks whether the external status checks pass. |
### `MergeabilityCheckStatus`

View File

@ -58,7 +58,7 @@ Supported attributes:
| ------------------------------- | -------------- | -------- | ----------- |
| `approved_by_ids` | integer array | No | Returns merge requests which have been approved by all the users with the given `id`. Maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. Premium and Ultimate only. |
| `approver_ids` | integer array | No | Returns merge requests which have specified all the users with the given `id` as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. Premium and Ultimate only. |
| `approved` | string | No | Filters merge requests by their `approved` status. `yes` returns only approved merge requests. `no` returns only non-approved merge requests. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3159) in GitLab 15.11. Available only when the feature flag `mr_approved_filter` is enabled. |
| `approved` | string | No | Filters merge requests by their `approved` status. `yes` returns only approved merge requests. `no` returns only non-approved merge requests. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3159) in GitLab 15.11 with the flag `mr_approved_filter`. Disabled by default. |
| `assignee_id` | integer | No | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
| `author_id` | integer | No | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`. |
| `author_username` | string | No | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. |
@ -841,6 +841,7 @@ Use `detailed_merge_status` instead of `merge_status` to account for all potenti
- `jira_association_missing`: The title or description must reference a Jira issue.
- `needs_rebase`: The merge request must be rebased.
- `conflict`: There are conflicts between the source and target branches.
- `requested_changes`: The merge request has reviewers who have requested changes.
### Preparation steps

View File

@ -33,7 +33,7 @@ This will allow us to transform GitLab into a generic automation tooling, but
will also reduce the complexity of existing events-like features:
1. [Webhooks](../../../user/project/integrations/webhook_events.md)
1. [Audit Events](../../../administration/audit_events.md)
1. [Audit Events](../../../administration/audit_event_reports.md)
1. [GitLab CI Events](https://about.gitlab.com/blog/2022/08/03/gitlab-ci-event-workflows/)
1. [Package Events](https://gitlab.com/groups/gitlab-org/-/epics/9677)
1. [GraphQL Events](https://gitlab.com/gitlab-org/gitlab/-/blob/dabf4783f5d758f69d947f5ff2391b4b1fb5f18a/app/graphql/graphql_triggers.rb)

View File

@ -1,11 +0,0 @@
---
redirect_to: '/ee/ci/components/#cicd-catalog'
remove_date: '2024-02-24'
---
This document was moved to [CI/CD components](index.md#cicd-catalog).
<!-- This redirect file can be deleted after <YYYY-MM-DD>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -21,8 +21,6 @@ earlier jobs it depends on finish running.
## Specify when jobs run with `rules`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27863) in GitLab 12.3.
Use [`rules`](../yaml/index.md#rules) to include or exclude jobs in pipelines.
Rules are evaluated in order until the first match. When a match is found, the job
@ -157,7 +155,6 @@ If the `Dockerfile` file or any file in `/docker/scripts` has changed **and** `$
then the job runs manually and is allowed to fail.
You can use [parentheses](#group-variable-expressions-together-with-parentheses) with `&&` and `||` to build more complicated variable expressions.
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230938) in GitLab 13.3:
```yaml
job1:
@ -167,10 +164,6 @@ job1:
- if: ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE
```
WARNING:
[Before GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/issues/230938),
rules that use both `||` and `&&` may evaluate with an unexpected order of operations.
### Avoid duplicate pipelines
If a job uses `rules`, a single action, like pushing a commit to a branch, can trigger
@ -274,7 +267,7 @@ check the value of the `$CI_PIPELINE_SOURCE` variable:
| `push` | For pipelines triggered by a `git push` event, including for branches and tags. |
| `schedule` | For [scheduled pipelines](../pipelines/schedules.md). |
| `trigger` | For pipelines created by using a [trigger token](../triggers/index.md#configure-cicd-jobs-to-run-in-triggered-pipelines). |
| `web` | For pipelines created by using **Run pipeline** button in the GitLab UI, from the project's **Build > Pipelines** section. |
| `web` | For pipelines created by selecting **Run pipeline** in the GitLab UI, from the project's **Build > Pipelines** section. |
| `webide` | For pipelines created by using the [WebIDE](../../user/project/web_ide/index.md). |
The following example runs the job as a manual job in scheduled pipelines or in push
@ -321,9 +314,6 @@ Other commonly used variables for `if` clauses:
### Variables in `rules:changes`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34272) in GitLab 13.6.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/267192) in GitLab 13.7.
You can use CI/CD variables in `rules:changes` expressions to determine when
to add jobs to a pipeline:
@ -370,208 +360,6 @@ job2:
- echo "It also runs for merge requests."
```
## Specify when jobs run with `only` and `except`
NOTE:
`only` and `except` are not being actively developed. [`rules`](#specify-when-jobs-run-with-rules)
is the preferred keyword to control when to add jobs to pipelines.
You can use [`only`](../yaml/index.md#only--except) and [`except`](../yaml/index.md#only--except)
to control when to add jobs to pipelines.
- Use `only` to define when a job runs.
- Use `except` to define when a job **does not** run.
### `only:refs` / `except:refs` examples
You can use `only` or `except` with:
- Specific keywords. See the full list in the [`only`/`except` syntax reference](../yaml/index.md#onlyrefs--exceptrefs).
- Branch names. Avoid branch names that are exactly the same as a specific keyword.
For example, jobs configured to run for the `tags` keyword (tag pipelines)
would also run for a branch named `tags`.
- Regex patterns to specify a range of branch names.
The following examples omit `refs` because `only` or `except` used without `refs`
is the same as [`only:refs` / `except/refs`](../yaml/index.md#onlyrefs--exceptrefs).
For example:
```yaml
job:
# use special keywords
only:
- tags
- triggers
- schedules
```
In this example, `job` runs only for:
- Git tags
- [Triggers](../triggers/index.md#configure-cicd-jobs-to-run-in-triggered-pipelines)
- [Scheduled pipelines](../pipelines/schedules.md)
To execute jobs only for the parent repository and not forks:
```yaml
job:
only:
- branches@gitlab-org/gitlab
except:
- main@gitlab-org/gitlab
- /^release/.*$/@gitlab-org/gitlab
```
This example runs `job` for all branches on `gitlab-org/gitlab`,
except `main` and branches that start with `release/`.
### `only: variables` / `except: variables` examples
You can use [`except:variables`](../yaml/index.md#onlyvariables--exceptvariables) to exclude jobs based on a commit message:
```yaml
end-to-end:
script: rake test:end-to-end
except:
variables:
- $CI_COMMIT_MESSAGE =~ /skip-end-to-end-tests/
```
You can use [parentheses](#group-variable-expressions-together-with-parentheses) with `&&` and `||`
to build more complicated variable expressions:
```yaml
job1:
script:
- echo This rule uses parentheses.
only:
variables:
- ($CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop") && $MY_VARIABLE
```
When multiple entries are specified in `only:variables`, the job runs when at least one of them evaluates to `true`.
You can use `&&` in a single entry when multiple conditions must be satisfied at the same time.
### `only:changes` / `except:changes` examples
You can skip a job if a change is detected in any file with a
`.md` extension in the root directory of the repository:
```yaml
build:
script: npm run build
except:
changes:
- "*.md"
```
If you change multiple files, but only one file ends in `.md`,
the `build` job is still skipped. The job does not run for any of the files.
With some configurations that use `changes`, [jobs or pipelines might run unexpectedly](job_troubleshooting.md#jobs-or-pipelines-run-unexpectedly-when-using-changes)
#### Use `only:changes` with merge request pipelines
With [merge request pipelines](../pipelines/merge_request_pipelines.md),
it's possible to define a job to be created based on files modified
in a merge request.
Use this keyword with `only: [merge_requests]` so GitLab can find the correct base
SHA of the source branch. File differences are correctly calculated from any further
commits, and all changes in the merge requests are properly tested in pipelines.
For example:
```yaml
docker build service one:
script: docker build -t my-service-one-image:$CI_COMMIT_REF_SLUG .
only:
refs:
- merge_requests
changes:
- Dockerfile
- service-one/**/*
```
In this scenario, if a merge request changes
files in the `service-one` directory or the `Dockerfile`, GitLab creates
the `docker build service one` job.
For example:
```yaml
docker build service one:
script: docker build -t my-service-one-image:$CI_COMMIT_REF_SLUG .
only:
changes:
- Dockerfile
- service-one/**/*
```
In this example, the pipeline might fail because of changes to a file in `service-one/**/*`.
A later commit that doesn't have changes in `service-one/**/*`
but does have changes to the `Dockerfile` can pass. The job
only tests the changes to the `Dockerfile`.
GitLab checks the **most recent pipeline** that **passed**. If the merge request is mergeable,
it doesn't matter that an earlier pipeline failed because of a change that has not been corrected.
When you use this configuration, ensure that the most recent pipeline
properly corrects any failures from previous pipelines.
### Combine multiple keywords with `only` or `except`
If you use multiple keywords with `only` or `except`, the keywords are evaluated
as a single conjoined expression. That is:
- `only` includes the job if **all** of the keys have at least one condition that matches.
- `except` excludes the job if **any** of the keys have at least one condition that matches.
With `only`, individual keys are logically joined by an `AND`. A job is added to
the pipeline if the following is true:
- `(any listed refs are true) AND (any listed variables are true) AND (any listed changes are true) AND (any chosen Kubernetes status matches)`
In the following example, the `test` job is only created when **all** of the following are true:
- The pipeline is [scheduled](../pipelines/schedules.md) **or** runs for `main`.
- The `variables` keyword matches.
- The `kubernetes` service is active on the project.
```yaml
test:
script: npm run test
only:
refs:
- main
- schedules
variables:
- $CI_COMMIT_MESSAGE =~ /run-end-to-end-tests/
kubernetes: active
```
With `except`, individual keys are logically joined by an `OR`. A job is **not**
added if the following is true:
- `(any listed refs are true) OR (any listed variables are true) OR (any listed changes are true) OR (a chosen Kubernetes status matches)`
In the following example, the `test` job is **not** created when **any** of the following are true:
- The pipeline runs for the `main` branch.
- There are changes to the `README.md` file in the root directory of the repository.
```yaml
test:
script: npm run test
except:
refs:
- main
changes:
- "README.md"
```
## Create a job that must be run manually
You can require that a job doesn't run unless a user starts it. This is called a **manual job**.
@ -653,7 +441,7 @@ To protect a manual job:
1. In the [protected environments settings](../environments/protected_environments.md#protecting-environments),
select the environment (`production` in this example) and add the users, roles or groups
that are authorized to trigger the manual job to the **Allowed to Deploy** list. Only those in
this list can trigger this manual job, as well as GitLab administrators
this list can trigger this manual job, and GitLab administrators
who are always able to use protected environments.
You can use protected environments with blocking manual jobs to have a list of users
@ -663,8 +451,6 @@ by authorized users.
## Run a job after a delay
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/51352) in GitLab 11.4.
Use [`when: delayed`](../yaml/index.md#when) to execute scripts after a waiting period, or if you want to avoid
jobs immediately entering the `pending` state.
@ -734,8 +520,6 @@ Test Boosters reports usage statistics to the author.
### Run a one-dimensional matrix of parallel jobs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26362) in GitLab 13.5.
You can create a one-dimensional matrix of parallel jobs:
```yaml
@ -753,8 +537,6 @@ You can also [create a multi-dimensional matrix](../yaml/index.md#parallelmatrix
### Run a matrix of parallel trigger jobs
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/270957) in GitLab 13.10.
You can run a [trigger](../yaml/index.md#trigger) job multiple times in parallel in a single pipeline,
but with different variable values for each instance of the job.
@ -931,7 +713,7 @@ types the variables can control for:
- Branch pipelines that run for Git `push` events to a branch, like new commits or tags.
- Tag pipelines that run only when a new Git tag is pushed to a branch.
- [Merge request pipelines](../pipelines/merge_request_pipelines.md) that run for changes
to a merge request, like new commits or selecting the **Run pipeline** button
to a merge request, like new commits or selecting **Run pipeline**
in a merge request's pipelines tab.
- [Scheduled pipelines](../pipelines/schedules.md).
@ -991,25 +773,8 @@ matching only a substring of the tag name or branch name.
For example, `/^issue-.*$/` is equivalent to `/^issue-/`,
while just `/issue/` would also match a branch called `severe-issues`.
### `only` / `except` regex syntax
In GitLab 11.9.4, GitLab began internally converting the regexp used
in `only` and `except` keywords to [RE2](https://github.com/google/re2/wiki/Syntax).
[RE2](https://github.com/google/re2/wiki/Syntax) limits the set of available features
due to computational complexity, and some features, like negative lookaheads, became unavailable.
Only a subset of features provided by [Ruby Regexp](https://ruby-doc.org/core/Regexp.html)
are now supported.
From GitLab 11.9.7 to GitLab 14.9, GitLab provided a feature flag to let you
use unsafe regexp syntax. We've fully migrated to RE2 now, and that feature
flag is no longer available.
## CI/CD variable expressions
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/37397) in GitLab 10.7 for [the `only` and `except` CI keywords](../yaml/index.md#onlyvariables--exceptvariables)
> - [Expanded](https://gitlab.com/gitlab-org/gitlab/-/issues/27863) in GitLab 12.3 with [the `rules` keyword](../yaml/index.md#rules)
Use variable expressions to control which jobs are created in a pipeline after changes
are pushed to GitLab. You can use variable expressions with:
@ -1125,8 +890,6 @@ regex-job2:
### Join variable expressions together with `&&` or `||`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62867) in GitLab 12.0
You can join multiple expressions using `&&` (and) or `||` (or), for example:
- `$VARIABLE1 =~ /^content.*/ && $VARIABLE2 == "something"`
@ -1138,9 +901,6 @@ so `&&` is evaluated before `||`.
#### Group variable expressions together with parentheses
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230938) in GitLab 13.3.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/238174) in GitLab 13.5.
You can use parentheses to group expressions together. Parentheses take precedence over
`&&` and `||`, so expressions enclosed in parentheses are evaluated first, and the
result is used for the rest of the expression.

View File

@ -2497,7 +2497,7 @@ before cloning the Git repository and any submodules.
You can use it for example to:
- Adjust the [Git configuration](../jobs/index.md#get_sources-job-section-fails-because-of-an-http2-problem).
- Export [tracing variables](../../topics/git/useful_git_commands.md).
- Export [tracing variables](../../topics/git/troubleshooting_git.md#debug-git-with-traces).
**Possible inputs**: An array including:
@ -5513,9 +5513,6 @@ You can use `only` and `except` to control when to add jobs to pipelines.
- Use `only` to define when a job runs.
- Use `except` to define when a job **does not** run.
See [specify when jobs run with `only` and `except`](../jobs/job_control.md#specify-when-jobs-run-with-only-and-except)
for more details and examples.
#### `only:refs` / `except:refs`
NOTE:
@ -5532,8 +5529,7 @@ pipeline based on branch names or pipeline types.
**Possible inputs**: An array including any number of:
- Branch names, for example `main` or `my-feature-branch`.
- [Regular expressions](../jobs/job_control.md#only--except-regex-syntax)
that match against branch names, for example `/^feature-.*/`.
- Regular expressions that match against branch names, for example `/^feature-.*/`.
- The following keywords:
| **Value** | **Description** |
@ -5635,10 +5631,6 @@ deploy:
- $STAGING
```
**Related topics**:
- [`only:variables` and `except:variables` examples](../jobs/job_control.md#only-variables--except-variables-examples).
#### `only:changes` / `except:changes`
`only:variables` and `except:variables`
@ -5656,7 +5648,7 @@ Use `changes` in pipelines with the following refs:
- `branches`
- `external_pull_requests`
- `merge_requests` (see additional details about [using `only:changes` with merge request pipelines](../jobs/job_control.md#use-onlychanges-with-merge-request-pipelines))
- `merge_requests`
**Keyword type**: Job keyword. You can use it only as part of a job.
@ -5700,9 +5692,6 @@ docker build:
**Related topics**:
- [`only: changes` and `except: changes` examples](../jobs/job_control.md#onlychanges--exceptchanges-examples).
- If you use `changes` with [only allow merge requests to be merged if the pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#require-a-successful-pipeline-for-merge),
you should [also use `only:merge_requests`](../jobs/job_control.md#use-onlychanges-with-merge-request-pipelines).
- [Jobs or pipelines can run unexpectedly when using `only: changes`](../jobs/job_troubleshooting.md#jobs-or-pipelines-run-unexpectedly-when-using-changes).
#### `only:kubernetes` / `except:kubernetes`

View File

@ -1,11 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2024-03-13'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after <2024-03-13>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,11 +0,0 @@
---
redirect_to: '../ci/runners/runner_fleet_dashboard.md'
remove_date: '2024-03-01'
---
This document was moved to [another location](../ci/runners/runner_fleet_dashboard.md).
<!-- This redirect file can be deleted after <2024-03-01>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -129,7 +129,7 @@ For example, use any of these trigger words to close the Jira issue `PROJECT-1`:
- `Fixes PROJECT-1`
The commit or merge request must target your project's [default branch](../../user/project/repository/branches/default.md).
You can change your project's default branch in [project settings](../../user/project/settings/index.md).
You can change your project's default branch in [project settings](../../user/project/repository/branches/default.md#change-the-default-branch-name-for-a-project).
When your branch name matches the Jira issue ID, `Closes <JIRA-ID>` is automatically appended to your existing merge request template.
If you do not want to close the issue, [disable automatic issue closing](../../user/project/issues/managing_issues.md#disable-automatic-issue-closing).

View File

@ -49,7 +49,7 @@ Security incidents related to credentials exposure can vary in severity from low
#### Event types
- Review the available [audit events](../administration/audit_events.md) for your group or namespace.
- Review the available [audit events](../administration/audit_event_reports.md) for your group or namespace.
- Adversaries may attempt to create tokens, SSH keys, or user accounts to maintain persistence. Look for [audit events](../administration/audit_event_streaming/audit_event_types.md) related to these activities.
- Focus on CI-related [audit events](../administration/audit_event_types.md#continuous-integration) to identify any modifications to CI/CD variables.
- Review [job logs](../administration/job_logs.md) for any pipelines ran by an adversary
@ -68,7 +68,7 @@ If you suspect that a user account or bot account has been compromised, you shou
#### Event types
Review the [audit events](../administration/audit_events.md) available to you to identify any suspicious account behavior. For example:
Review the [audit events](../administration/audit_event_reports.md) available to you to identify any suspicious account behavior. For example:
- Suspicious sign-in events.
- Creation or deletion of personal, project, and group access tokens.
@ -100,7 +100,7 @@ Under normal circumstances, the `CI_JOB_TOKEN` is not displayed in the job logs.
- Check if there are any recent modifications to the source code in the repo. You can check the commit history of the modified file to determine the actor who made the changes. If you suspect suspicious edits, investigate the user activity using the [suspected compromised user account guide](#suspected-compromised-user-account).
- Any suspicious modification to any code that is called by that file can cause issues and should be investigated and may lead to exposed secrets.
- Consider rotating the exposed secrets after determining the production impact of revocation.
- Review [audit logs](../administration/audit_events.md) available to you for any suspicious modifications to user and project settings.
- Review [audit logs](../administration/audit_event_reports.md) available to you for any suspicious modifications to user and project settings.
##### Secrets exposed through misconfigured GitLab CI/CD
@ -128,7 +128,7 @@ It is important to [regularly update GitLab](../policy/maintenance.md), update y
If you suspect that your GitLab instance has been compromised, you should:
- Review the [audit events](../administration/audit_events.md) available to you for suspicious account behavior.
- Review the [audit events](../administration/audit_event_reports.md) available to you for suspicious account behavior.
- Review [all users](../administration/moderate_users.md) (including the Administrative root user), and follow the steps in the [suspected compromised user account guide](#suspected-compromised-user-account) if necessary.
- Review the Credentials Inventory, if available to you.
- Change any sensitive credentials, variables, tokens, and secrets. For example, those located in instance configuration, database, CI/CD pipelines, or elsewhere.
@ -161,7 +161,7 @@ Security incidents can occur as a result of improperly configured project or gro
If you suspect unauthorized modifications to project settings, consider taking the following steps:
- Begin by reviewing the available [audit events](../administration/audit_events.md) to identify the user responsible for the action.
- Begin by reviewing the available [audit events](../administration/audit_event_reports.md) to identify the user responsible for the action.
- If the user account appears suspicious, follow the steps outlined in the [suspected compromised user account guide](#suspected-compromised-user-account).
- Consider reverting the settings to their original state by referring to the audit events and consulting the project owners and maintainers for guidance.

View File

@ -185,3 +185,15 @@ To work around this error:
1. Revert the change so the error message `Help page documentation base url is blocked` does not appear anymore.
1. Add `docs.gitlab.com` , or [the redirect help documentation pages URL](../administration/settings/help_page.md#redirect-help-pages) to the [allowlist](#allow-outbound-requests-to-certain-ip-addresses-and-domains).
1. Select **Save Changes**.
### GitLab Duo functionality is blocked
When you [filter requests](#filter-requests), you might see `401` errors when trying to use [GitLab Duo features](../user/ai_features.md).
This error can occur when outbound requests to the GitLab cloud server are not allowed. To work around this error:
1. Add `https://cloud.gitlab.com:443` to the [allowlist](#allow-outbound-requests-to-certain-ip-addresses-and-domains).
1. Select **Save Changes**.
1. After GitLab has access to the [cloud server](../user/ai_features.md), [manually sychronize your license](../subscriptions/self_managed/index.md#manually-synchronize-your-subscription-details)
For more information, see the [GitLab Duo Code Suggestions troubleshooting documentation](../user/project/repository/code_suggestions/troubleshooting.md).

View File

@ -76,7 +76,7 @@ the tiers are no longer mentioned in GitLab documentation:
- [Contribution Analytics](../user/group/contribution_analytics/index.md)
- [Merge Request Analytics](../user/analytics/merge_request_analytics.md)
- [Code Review Analytics](../user/analytics/code_review_analytics.md)
- [Audit Events](../administration/audit_events.md)
- [Audit Events](../administration/audit_event_reports.md)
- Rake tasks:
- [Displaying GitLab license information](../administration/raketasks/maintenance.md#show-gitlab-license-information)
- Reference Architecture information:

View File

@ -120,6 +120,5 @@ your repository small:
- Official [Git documentation](https://git-scm.com), including
[Git on the Server - GitLab](https://git-scm.com/book/en/v2/Git-on-the-Server-GitLab)
- [Git troubleshooting](troubleshooting_git.md) techniques
- [Git commands](useful_git_commands.md) collected by the GitLab support team
- Blog post: [Git Tips & Tricks](https://about.gitlab.com/blog/2016/12/08/git-tips-and-tricks/)
- Blog post: [Eight Tips to help you work better with Git](https://about.gitlab.com/blog/2015/02/19/8-tips-to-help-you-work-better-with-git/)

View File

@ -139,7 +139,7 @@ It is possible to host LFS objects externally by setting a custom LFS URL with `
You might choose to do this if you are using an appliance like a Nexus Repository to store LFS data. If you choose to use an external LFS store,
GitLab can't verify LFS objects. Pushes then fail if you have GitLab LFS support enabled.
To stop push failure, LFS support can be disabled in the [Project settings](../../../user/project/settings/index.md), which also disables GitLab LFS value-adds (Verifying LFS objects, UI integration for LFS).
To stop push failure, LFS support can be disabled in the [Project settings](index.md#enable-git-lfs-for-a-project), which also disables GitLab LFS value-adds (Verifying LFS objects, UI integration for LFS).
## I/O timeout when pushing LFS objects

View File

@ -1,11 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2024-03-19'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after <2024-03-19>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -22,9 +22,9 @@ Projects that depend on the affected components have new vulnerabilities automat
Continuous Vulnerability Scanning detects vulnerabilities in the latest CycloneDX SBOM reports for the default branch.
[Dependency Scanning](../dependency_scanning/index.md) and [Container Scanning](../container_scanning/index.md) are used to generate these reports.
## Supported component types
## Supported package types
Components with the following [PURL types](https://github.com/package-url/purl-spec/blob/346589846130317464b677bc4eab30bf5040183a/PURL-TYPES.rst) are supported:
Components with the following [package URL types](https://github.com/package-url/purl-spec/blob/346589846130317464b677bc4eab30bf5040183a/PURL-TYPES.rst) are supported:
- `composer`
- `conan`
@ -36,7 +36,10 @@ Components with the following [PURL types](https://github.com/package-url/purl-s
- `nuget`
- `pypi`
Work to support `apk` and `rpm` PURL types is tracked in [issue 428703](https://gitlab.com/gitlab-org/gitlab/-/issues/428703).
Work to support `apk` and `rpm` package URL types is tracked in [issue 428703](https://gitlab.com/gitlab-org/gitlab/-/issues/428703).
Go pseudo versions are not supported. A project dependency that references a Go pseudo version is
never considered as affected because this might result in false negatives.
## Configuration
@ -51,13 +54,6 @@ For self-managed GitLab instances in an environment with limited, restricted, or
some adjustments are required to successfully scan CycloneDX reports for vulnerabilities.
For more information, see the offline [quick start guide](../../../topics/offline/quick_start_guide.md#enabling-the-package-metadata-database).
## Supported languages and package managers
The supported files and versions are the ones supported by
[Dependency Scanning](../dependency_scanning/index.md#supported-languages-and-package-managers).
Go pseudo versions are not supported. A project dependency that references a Go pseudo version is never considered as affected. This might result in false negatives.
## Checking new vulnerabilities
New vulnerabilities detected by Continuous Vulnerability Scanning are visible on the [Vulnerability Report](../vulnerability_report/index.md).

View File

@ -410,4 +410,4 @@ To delete a scanner profile:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217872) in GitLab 14.1.
The creation, updating, and deletion of DAST profiles, DAST scanner profiles,
and DAST site profiles are included in the [audit log](../../../administration/audit_events.md).
and DAST site profiles are included in the [audit log](../../../administration/audit_event_reports.md).

View File

@ -282,12 +282,12 @@ Without the manual synchronization, it might take up to 24 hours to activate Git
## Use GitLab Duo Chat in the GitLab UI
1. In the lower-left corner, select the **Help** icon.
The [new left sidebar must be enabled](../tutorials/left_sidebar/index.md).
1. Select **GitLab Duo Chat**. A drawer opens on the right side of your screen.
1. In the upper-right corner, select **GitLab Duo Chat**. A drawer opens on the right side of your screen.
1. Enter your question in the chat input box and press **Enter** or select **Send**. It may take a few seconds for the interactive AI chat to produce an answer.
1. You can ask a follow-up question.
1. If you want to ask a new question unrelated to the previous conversation, you may receive better answers if you clear the context by typing `/reset` into the input box and selecting **Send**.
1. Optional. Ask a follow-up question.
To ask a new question unrelated to the previous conversation, you might receive better answers
if you clear the context by typing `/reset` and selecting **Send**.
NOTE:
Only the last 50 messages are retained in the chat history. The chat history expires 3 days after last use.

View File

@ -253,7 +253,7 @@ DETAILS:
As a group Owner, you can prevent any new project membership for all
projects in a group, allowing tighter control over project membership.
For example, if you want to lock the group for an [Audit Event](../../administration/audit_events.md),
For example, if you want to lock the group for an [Audit Event](../../administration/audit_event_reports.md),
you can guarantee that project membership cannot be modified during the audit.
If group membership lock is enabled, the group Owner can still:

View File

@ -305,7 +305,7 @@ As outlined in the [user access section](index.md#link-saml-to-your-existing-git
If the top-level group has [restricted membership by email domain](../access_and_permissions.md#restrict-group-access-by-domain), and a user with an email domain that is not allowed tries to sign in with SSO, that user might receive a 404. Users might have multiple accounts, and their SAML identity might be linked to their personal account which has an email address that is different than the company domain. To check this, verify the following:
- That the top-level group has restricted membership by email domain.
- That, in [Audit Events](../../../administration/audit_events.md) for the top-level group:
- That, in [Audit Events](../../../administration/audit_event_reports.md) for the top-level group:
- You can see **Signed in with GROUP_SAML authentication** action for that user.
- That the user's username is the same as the username you configured for SAML SSO, by selecting the **Author** name.
- If the username is different to the username you configured for SAML SSO, ask the user to [unlink the SAML identity](index.md#unlink-accounts) from their personal account.

View File

@ -167,7 +167,7 @@ The following table lists project permissions available for each role:
| [Projects](project/index.md):<br>Create, edit, delete [releases](project/releases/index.md) | | | ✓ | ✓ | ✓ | If the [tag is protected](project/protected_tags.md), this depends on the access given to Developers and Maintainers. |
| [Projects](project/index.md):<br>Create, edit [wiki](project/wiki/index.md) pages | | | ✓ | ✓ | ✓ | |
| [Projects](project/index.md):<br>Enable [Review Apps](../ci/review_apps/index.md) | | | ✓ | ✓ | ✓ | |
| [Projects](project/index.md):<br>View project [Audit Events](../administration/audit_events.md) | | | ✓ | ✓ | ✓ | Users can only view events based on their individual actions. |
| [Projects](project/index.md):<br>View project [Audit Events](../administration/audit_event_reports.md) | | | ✓ | ✓ | ✓ | Users can only view events based on their individual actions. |
| [Projects](project/index.md):<br>Add [deploy keys](project/deploy_keys/index.md) | | | | ✓ | ✓ | |
| [Projects](project/index.md):<br>Add new [team members](project/members/index.md) | | | | ✓ | ✓ | |
| [Projects](project/index.md):<br>Manage [team members](project/members/index.md) | | | | ✓ | ✓ | Maintainers cannot create, demote, or remove Owners, and they cannot promote users to the Owner role. They also cannot approve Owner role access requests. |

View File

@ -71,8 +71,11 @@ DETAILS:
GitLab self-managed instance administrators can define [custom merge drivers](https://git-scm.com/docs/gitattributes#_defining_a_custom_merge_driver)
in a GitLab configuration file, then use the custom merge drivers in a Git `.gitattributes` file. Custom merge drivers are not supported on GitLab.com.
You might configure a custom merge driver, for example, if there are certain
files that should be ignored during a merge such as build files and configuration files.
Custom merge drivers are a Git feature that gives you advanced control over conflict
resolution.
A custom merge driver is invoked only in the case of a non-trivial
[merge conflict](merge_requests/conflicts.md), so it is not a reliable way
of preventing some files from being merged.
### Configure a custom merge driver

View File

@ -322,7 +322,7 @@ The default issue closing pattern regex:
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/240922) in GitLab 15.4: The referenced issue's project setting is checked instead of the project of the commit or merge request.
You can disable the automatic issue closing feature on a per-project basis
in the [project's settings](../settings/index.md).
in the [project's settings](#disable-automatic-issue-closing).
Prerequisites:

View File

@ -18,7 +18,7 @@ GitLab does not limit the number of private projects you can create.
- [Create a project](index.md)
- [Manage projects](working_with_projects.md)
- [Project visibility](../public_access.md)
- [Project settings](../project/settings/index.md)
- [Project settings](working_with_projects.md)
- [Project access tokens](../project/settings/project_access_tokens.md)
- [Share projects](../project/members/share_project_with_groups.md)
- [Reserved project and group names](../../user/reserved_names.md)

View File

@ -86,10 +86,6 @@ module API
not_found! unless success
::MergeRequests::UpdateReviewerStateService
.new(project: user_project, current_user: current_user)
.execute(merge_request, "unreviewed")
present_approval(merge_request)
end

View File

@ -1,5 +1,5 @@
variables:
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.86.0'
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.87.0'
.dast-auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.86.0'
AUTO_DEPLOY_IMAGE_VERSION: 'v2.87.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -1,5 +1,5 @@
variables:
AUTO_DEPLOY_IMAGE_VERSION: 'v2.86.0'
AUTO_DEPLOY_IMAGE_VERSION: 'v2.87.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"

View File

@ -15,7 +15,7 @@
variables:
# OpenTofu CI/CD component version, see https://gitlab.com/components/opentofu/-/releases
VERSION: "latest"
GITLAB_OPENTOFU_VERSION: "latest"
# Compatible OpenTofu version, see https://gitlab.com/components/opentofu/-/releases
OPENTOFU_VERSION: "1.6.0"
# Job Image with `gitlab-tofu`

View File

@ -270,10 +270,6 @@ module Gitlab
next unless success
::MergeRequests::UpdateReviewerStateService
.new(project: quick_action_target.project, current_user: current_user)
.execute(quick_action_target, "unreviewed")
@execution_message[:unapprove] = _('Unapproved the current merge request.')
end

View File

@ -32212,6 +32212,9 @@ msgstr ""
msgid "MlExperimentTracking|Experiment"
msgstr ""
msgid "MlExperimentTracking|Experiment metadata"
msgstr ""
msgid "MlExperimentTracking|Experiment removed"
msgstr ""
@ -32251,6 +32254,9 @@ msgstr ""
msgid "MlExperimentTracking|No candidates logged for the query. Create new candidates using the MLflow client."
msgstr ""
msgid "MlExperimentTracking|No logged experiment metadata"
msgstr ""
msgid "MlExperimentTracking|No name"
msgstr ""
@ -41214,6 +41220,9 @@ msgstr ""
msgid "Purchase more storage"
msgstr ""
msgid "Purchase seats"
msgstr ""
msgid "PurchaseStep|An error occurred in the purchase step. If the problem persists please contact support at https://support.gitlab.com."
msgstr ""
@ -48325,6 +48334,9 @@ msgstr ""
msgid "Snippets|Files"
msgstr ""
msgid "Snippets|Snippet actions"
msgstr ""
msgid "Snippets|Snippets are limited to %{total} files."
msgstr ""

View File

@ -59,7 +59,7 @@
"@gitlab/cluster-client": "^2.1.0",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
"@gitlab/svgs": "3.91.0",
"@gitlab/svgs": "3.93.0",
"@gitlab/ui": "78.6.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "^0.0.1-dev-20240226152102",

View File

@ -172,7 +172,8 @@ module QA
end
def click_delete_button
click_element('snippet-action-button', action: 'Delete')
click_element('snippets-more-actions-dropdown-toggle')
click_button('Delete')
click_element('delete-snippet-button')
# wait for the page to reload after deletion
wait_until(reload: false) do

View File

@ -19,7 +19,8 @@ module QA
project.visit!
Page::Project::Show.perform do |project|
Support::Waiter.wait_until(reload_page: project, message: 'Waiting for licence') do
Support::Waiter.wait_until(reload_page: project, retry_on_exception: true,
message: 'Waiting for licence') do
project.has_license?(rendered_license_name)
end
end

View File

@ -26,6 +26,33 @@ RSpec.describe SearchController, feature_category: :global_search do
end
end
shared_examples_for 'metadata is set' do |action|
it 'renders a 408 when a timeout occurs' do
expect(controller).to receive(:append_info_to_payload).and_wrap_original do |method, payload|
method.call(payload)
expect(payload[:metadata]['meta.search.group_id']).to eq('123')
expect(payload[:metadata]['meta.search.project_id']).to eq('456')
expect(payload[:metadata]).not_to have_key('meta.search.search')
expect(payload[:metadata]['meta.search.scope']).to eq('issues')
expect(payload[:metadata]['meta.search.force_search_results']).to eq('true')
expect(payload[:metadata]['meta.search.filters.confidential']).to eq('true')
expect(payload[:metadata]['meta.search.filters.state']).to eq('true')
expect(payload[:metadata]['meta.search.project_ids']).to eq(%w[456 789])
expect(payload[:metadata]['meta.search.type']).to eq('basic')
expect(payload[:metadata]['meta.search.level']).to eq('global')
expect(payload[:metadata]['meta.search.filters.language']).to eq('ruby')
expect(payload[:metadata]['meta.search.page']).to eq('2')
expect(payload[:metadata][:global_search_duration_s]).to be_a_kind_of(Numeric)
end
params = {
scope: 'issues', search: 'hello world', group_id: '123', page: '2', project_id: '456', language: 'ruby',
project_ids: %w[456 789], confidential: true, include_archived: true, state: true, force_search_results: true
}
get action, params: params
end
end
describe 'GET #show', :snowplow do
it_behaves_like 'when the user cannot read cross project', :show, { search: 'hello' } do
it 'still allows accessing the search page' do
@ -37,6 +64,7 @@ RSpec.describe SearchController, feature_category: :global_search do
it_behaves_like 'with external authorization service enabled', :show, { search: 'hello' }
it_behaves_like 'support for active record query timeouts', :show, { search: 'hello' }, :search_objects, :html
it_behaves_like 'metadata is set', :show
describe 'rate limit scope' do
it 'uses current_user and search scope' do
@ -387,6 +415,7 @@ RSpec.describe SearchController, feature_category: :global_search do
it_behaves_like 'when the user cannot read cross project', :count, { search: 'hello', scope: 'projects' }
it_behaves_like 'with external authorization service enabled', :count, { search: 'hello', scope: 'projects' }
it_behaves_like 'support for active record query timeouts', :count, { search: 'hello', scope: 'projects' }, :search_results, :json
it_behaves_like 'metadata is set', :count
it 'returns the result count for the given term and scope' do
create(:project, :public, name: 'hello world')

View File

@ -126,6 +126,7 @@ FactoryBot.define do
jira_issue_prefix { '' }
jira_issue_regex { '' }
project_key { nil }
project_keys { [] }
vulnerabilities_enabled { false }
vulnerabilities_issuetype { nil }
deployment_type { 'cloud' }
@ -143,8 +144,10 @@ FactoryBot.define do
jira_issue_prefix: evaluator.jira_issue_prefix,
jira_issue_regex: evaluator.jira_issue_regex,
username: evaluator.username, password: evaluator.password, issues_enabled: evaluator.issues_enabled,
project_key: evaluator.project_key, vulnerabilities_enabled: evaluator.vulnerabilities_enabled,
vulnerabilities_issuetype: evaluator.vulnerabilities_issuetype, deployment_type: evaluator.deployment_type
project_key: evaluator.project_key, project_keys: evaluator.project_keys,
vulnerabilities_enabled: evaluator.vulnerabilities_enabled,
vulnerabilities_issuetype: evaluator.vulnerabilities_issuetype,
deployment_type: evaluator.deployment_type
)
end
end

View File

@ -3,8 +3,10 @@
require 'spec_helper'
RSpec.describe 'Projects > Snippets > User deletes a snippet', :js, feature_category: :source_code_management do
include Spec::Support::Helpers::ModalHelpers
let(:project) { create(:project) }
let!(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let(:user) { create(:user) }
before do
@ -17,12 +19,22 @@ RSpec.describe 'Projects > Snippets > User deletes a snippet', :js, feature_cate
it 'deletes a snippet' do
expect(page).to have_content(snippet.title)
click_button('Delete')
click_button('Delete snippet')
click_on 'Snippet actions'
page.within(find_by_testid('snippets-more-actions-dropdown')) do
click_on 'Delete'
end
within_modal do
click_button 'Delete snippet'
end
wait_for_requests
# This assertion also confirms we did not end up on an error page
expect(page).to have_selector('#new_snippet_link')
expect(current_url).to end_with(project_snippets_path(project))
expect(page).to have_link('New snippet')
expect(page).not_to have_content(snippet.title)
expect(project.snippets.length).to eq(0)
end
end

View File

@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User deletes snippet', :js, feature_category: :source_code_management do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) }
let(:content) { 'puts "test"' }
let(:snippet) { create(:personal_snippet, :repository, :public, content: content, author: user) }
@ -16,10 +18,19 @@ RSpec.describe 'User deletes snippet', :js, feature_category: :source_code_manag
it 'deletes the snippet' do
expect(page).to have_content(snippet.title)
click_button('Delete')
click_button('Delete snippet')
click_on 'Snippet actions'
page.within(find_by_testid('snippets-more-actions-dropdown')) do
click_on 'Delete'
end
within_modal do
click_button 'Delete snippet'
end
wait_for_requests
expect(page).to have_link('New snippet')
expect(page).not_to have_content(snippet.title)
end
end

View File

@ -7,7 +7,13 @@ import RegistrySearch from '~/vue_shared/components/registry/registry_search.vue
import Pagination from '~/vue_shared/components/incubation/pagination.vue';
import setWindowLocation from 'helpers/set_window_location_helper';
import * as urlHelpers from '~/lib/utils/url_utility';
import { MOCK_START_CURSOR, MOCK_PAGE_INFO, MOCK_CANDIDATES, MOCK_EXPERIMENT } from './mock_data';
import {
MOCK_START_CURSOR,
MOCK_PAGE_INFO,
MOCK_CANDIDATES,
MOCK_EXPERIMENT,
MOCK_EXPERIMENT_METADATA,
} from './mock_data';
describe('MlExperimentsShow', () => {
let wrapper;
@ -29,6 +35,17 @@ describe('MlExperimentsShow', () => {
createWrapper(MOCK_CANDIDATES, ['rmse', 'auc', 'mae'], ['l1_ratio'], pageInfo);
};
const createWrapperWithExperimentMetadata = () => {
createWrapper(
[],
[],
[],
MOCK_PAGE_INFO,
{ ...MOCK_EXPERIMENT, metadata: MOCK_EXPERIMENT_METADATA },
'path',
);
};
const findPagination = () => wrapper.findComponent(Pagination);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findRegistrySearch = () => wrapper.findComponent(RegistrySearch);
@ -40,6 +57,10 @@ describe('MlExperimentsShow', () => {
const findExperimentHeader = () => wrapper.findComponent(ModelExperimentsHeader);
const findDeleteButton = () => wrapper.findComponent(DeleteButton);
const findDownloadButton = () => findExperimentHeader().findComponent(GlButton);
const findMetadataTableRow = (idx) => wrapper.findAll('.experiment-metadata tbody > tr').at(idx);
const findMetadataTableColumn = (row, col) => findMetadataTableRow(row).findAll('td').at(col);
const findMetadataHeader = () => wrapper.find('.experiment-metadata h3');
const findMetadataEmptyState = () => wrapper.find('.experiment-metadata .gl-text-secondary');
const hrefInRowAndColumn = (row, col) =>
findColumnInRow(row, col).findComponent(GlLink).attributes().href;
@ -318,4 +339,27 @@ describe('MlExperimentsShow', () => {
});
});
});
describe('Experiments metadata', () => {
it('has correct header', () => {
createWrapper();
expect(findMetadataHeader().text()).toBe('Experiment metadata');
});
it('shows empty state if there is no metadata', () => {
createWrapper();
expect(findMetadataEmptyState().text()).toBe('No logged experiment metadata');
});
it('shows the metadata', () => {
createWrapperWithExperimentMetadata();
MOCK_EXPERIMENT_METADATA.forEach((metadata, idx) => {
expect(findMetadataTableColumn(idx, 0).text()).toContain(metadata.name);
expect(findMetadataTableColumn(idx, 1).text()).toContain(metadata.value);
});
});
});
});

View File

@ -7,7 +7,30 @@ export const MOCK_PAGE_INFO = {
hasPreviousPage: true,
};
export const MOCK_EXPERIMENT = { name: 'experiment', path: '/path/to/experiment' };
export const MOCK_EXPERIMENT = {
name: 'experiment',
metadata: [],
path: '/path/to/experiment',
};
export const MOCK_EXPERIMENT_METADATA = [
{
id: 1,
created_at: '2024-03-20T16:19:23.843Z',
updated_at: '2024-03-20T16:19:23.843Z',
experiment_id: 1,
name: 'metadata_1',
value: 'a',
},
{
id: 2,
created_at: '2024-03-20T16:19:23.848Z',
updated_at: '2024-03-20T16:19:23.848Z',
experiment_id: 1,
name: 'metadata_2',
value: 'b',
},
];
export const MOCK_CANDIDATES = [
{

View File

@ -1,8 +1,14 @@
import { GlModal, GlButton, GlDropdown } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import {
GlModal,
GlButton,
GlDisclosureDropdown,
GlDisclosureDropdownGroup,
GlDisclosureDropdownItem,
} from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import MockAdapter from 'axios-mock-adapter';
import Vue, { nextTick } from 'vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
@ -60,7 +66,7 @@ describe('Snippet header component', () => {
[DeleteSnippetMutation, deleteSnippetMock],
]);
wrapper = mount(SnippetHeader, {
wrapper = mountExtended(SnippetHeader, {
provide: {
reportAbusePath,
canReportSpam,
@ -73,33 +79,24 @@ describe('Snippet header component', () => {
},
stubs: {
GlEmoji,
GlButton,
GlDisclosureDropdown,
GlDisclosureDropdownGroup,
GlDisclosureDropdownItem,
},
apolloProvider: mockApollo,
});
}
const findAuthorEmoji = () => wrapper.findComponent(GlEmoji);
const findAuthoredMessage = () => wrapper.find('[data-testid="authored-message"]').text();
const findAuthorUsername = () => wrapper.find('[data-testid="authored-username"]');
const findButtons = () => wrapper.findAllComponents(GlButton);
const findButtonsAsModel = () =>
findButtons().wrappers.map((x) => ({
text: x.text(),
href: x.attributes('href'),
category: x.props('category'),
variant: x.props('variant'),
disabled: x.props('disabled'),
}));
const findResponsiveDropdown = () => wrapper.findComponent(GlDropdown);
// We can't search by component here since we are full mounting and the attributes are applied to a child of the GlDropdownItem
const findResponsiveDropdownItems = () => findResponsiveDropdown().findAll('[role="menuitem"]');
const findResponsiveDropdownItemsAsModel = () =>
findResponsiveDropdownItems().wrappers.map((x) => ({
disabled: x.attributes('disabled'),
href: x.attributes('href'),
title: x.attributes('title'),
text: x.text(),
}));
const findAuthoredMessage = () => wrapper.findByTestId('authored-message').text();
const findAuthorUsername = () => wrapper.findByTestId('authored-username');
const findEditButton = () => wrapper.findByTestId('snippet-action-button');
const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const findDropdownItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
const findDropdownItemAt = (i) => findDropdownItems().at(i).props('item');
const findSpamAction = () => wrapper.findByText('Submit as spam');
const findDeleteAction = () => wrapper.findByText('Delete');
const findDeleteModal = () => wrapper.findComponent(GlModal);
beforeEach(() => {
@ -185,54 +182,27 @@ describe('Snippet header component', () => {
expect(text).toBe('Authored 1 month ago');
});
it('renders a action buttons', () => {
it('renders an edit button on sm and up screens', () => {
createComponent();
expect(findButtonsAsModel()).toEqual([
{
category: 'primary',
disabled: false,
href: `${snippet.webUrl}/edit`,
text: 'Edit',
variant: 'default',
},
{
category: 'secondary',
disabled: false,
text: 'Delete',
variant: 'danger',
},
{
category: 'primary',
disabled: false,
text: 'Submit as spam',
variant: 'default',
},
]);
expect(findEditButton().attributes('href')).toEqual(`${snippet.webUrl}/edit`);
expect(findEditButton().attributes('class')).toContain('gl-display-none');
expect(findEditButton().attributes('class')).toContain('gl-sm-display-inline-block');
});
it('renders responsive dropdown for action buttons', () => {
it('renders dropdown for action buttons', () => {
createComponent();
expect(findResponsiveDropdownItemsAsModel()).toEqual([
{
href: `${snippet.webUrl}/edit`,
text: 'Edit',
},
{
text: 'Delete',
},
{
text: 'Submit as spam',
title: 'Submit as spam',
},
]);
expect(findDropdownItemAt(0).text).toBe('Edit');
expect(findDropdownItemAt(0).href).toBe(`${snippet.webUrl}/edit`);
expect(findDropdownItemAt(1).text).toBe('Submit as spam');
expect(findDropdownItemAt(2).text).toBe('Delete');
});
it.each`
permissions | buttons
${{ adminSnippet: false, updateSnippet: false }} | ${['Submit as spam']}
${{ adminSnippet: true, updateSnippet: false }} | ${['Delete', 'Submit as spam']}
${{ adminSnippet: true, updateSnippet: false }} | ${['Submit as spam', 'Delete']}
${{ adminSnippet: false, updateSnippet: true }} | ${['Edit', 'Submit as spam']}
`('with permissions ($permissions), renders buttons ($buttons)', ({ permissions, buttons }) => {
createComponent({
@ -241,10 +211,10 @@ describe('Snippet header component', () => {
},
});
expect(findButtonsAsModel().map((x) => x.text)).toEqual(buttons);
expect(findDropdownItems().wrappers.map((x) => x.props('item').text)).toEqual(buttons);
});
it('with canCreateSnippet permission, renders create button', async () => {
it('with canCreateSnippet permission, renders new snippet button', async () => {
createComponent({
canCreateProjectSnippetMock: jest
.fn()
@ -256,17 +226,8 @@ describe('Snippet header component', () => {
await waitForPromises();
expect(findButtonsAsModel()).toEqual(
expect.arrayContaining([
{
category: 'secondary',
disabled: false,
href: `/foo/-/snippets/new`,
text: 'New snippet',
variant: 'confirm',
},
]),
);
expect(findDropdownItemAt(1).text).toBe('New snippet');
expect(findDropdownItemAt(1).href).toBe('/foo/-/snippets/new');
});
describe('submit snippet as spam', () => {
@ -281,9 +242,9 @@ describe('Snippet header component', () => {
`(
'renders a "$variant" alert message with "$text" message for a request with a "$request" response',
async ({ request, variant, text }) => {
const submitAsSpamBtn = findButtons().at(2);
mock.onPost(reportAbusePath).reply(request);
submitAsSpamBtn.trigger('click');
findDropdown().trigger('click');
findSpamAction().trigger('click');
await waitForPromises();
expect(createAlert).toHaveBeenLastCalledWith({
@ -309,11 +270,11 @@ describe('Snippet header component', () => {
});
it('does not show any action buttons', () => {
expect(findButtons()).toHaveLength(0);
expect(findEditButton().exists()).toBe(false);
});
it('does not show responsive action dropdown', () => {
expect(findResponsiveDropdown().exists()).toBe(false);
it('does not show action dropdown', () => {
expect(findDropdown().exists()).toBe(false);
});
});
@ -339,13 +300,16 @@ describe('Snippet header component', () => {
describe('Delete mutation', () => {
const deleteSnippet = async () => {
// Click delete action
findButtons().at(1).trigger('click');
findDropdown().trigger('click');
findDeleteAction().trigger('click');
await nextTick();
expect(findDeleteModal().props().visible).toBe(true);
// Click delete button in delete modal
document.querySelector('[data-testid="delete-snippet-button"').click();
await waitForPromises();
};

View File

@ -16,6 +16,14 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewState'] do
'REQUESTED_CHANGES' => have_attributes(
description: 'Merge request reviewer has requested changes.',
value: 'requested_changes'
),
'APPROVED' => have_attributes(
description: 'Merge request reviewer has approved the changes.',
value: 'approved'
),
'UNAPPROVED' => have_attributes(
description: 'Merge request reviewer removed their approval of the changes.',
value: 'unapproved'
)
)
end

View File

@ -106,9 +106,11 @@ RSpec.describe Projects::Ml::ExperimentsHelper, feature_category: :mlops do
subject { Gitlab::Json.parse(helper.experiment_as_data(experiment)) }
it do
is_expected.to eq(
{ 'name' => experiment.name, 'path' => "/#{project.full_path}/-/ml/experiments/#{experiment.iid}" }
)
is_expected.to eq({
'name' => experiment.name,
'metadata' => experiment.metadata,
'path' => "/#{project.full_path}/-/ml/experiments/#{experiment.iid}"
})
end
end

View File

@ -1411,4 +1411,10 @@ RSpec.describe Integrations::Jira, feature_category: :integrations do
end
end
end
describe '#project_keys_as_string' do
it 'returns comma separated project_keys' do
expect(jira_integration.project_keys_as_string).to eq 'TEST1,TEST2'
end
end
end

View File

@ -6423,4 +6423,30 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
it { is_expected.to eq(second_to_last_merge_request_diff) }
end
end
describe '#has_changes_requested?' do
let_it_be(:merge_request) { create(:merge_request, reviewers: [create(:user)]) }
context 'reviews have requested changed' do
before_all do
merge_request.merge_request_reviewers.first.update!(state: :requested_changes)
end
it { expect(merge_request.has_changes_requested?).to be_truthy }
end
context 'reviews have not requested changed' do
it { expect(merge_request.has_changes_requested?).to be_falsey }
end
end
describe '#batch_update_reviewer_state' do
let_it_be(:merge_request) { create(:merge_request, reviewers: create_list(:user, 2)) }
it 'updates all reviewers' do
user_ids = merge_request.reviewers.map(&:id)
expect { merge_request.batch_update_reviewer_state(user_ids, :reviewed) }.to change { merge_request.merge_request_reviewers.reload.all?(&:reviewed?) }.from(false).to(true)
end
end
end

View File

@ -232,7 +232,7 @@ RSpec.describe 'Query.project.pipeline', feature_category: :continuous_integrati
create(:ci_build_need, build: test_job, name: 'my test job')
end
it 'reports the build needs and execution requirements' do
it 'reports the build needs and execution requirements', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/448867' do
post_graphql(query, current_user: user)
expect(jobs_graphql_data).to contain_exactly(

View File

@ -413,19 +413,19 @@ RSpec.describe 'getting merge request information nested in a project', feature_
project.add_maintainer(user)
assign_user(user)
r = merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user)
r.update!(state: 'reviewed')
r.update!(state: :approved)
merge_request.approved_by_users << user
end
it 'returns appropriate data' do
post_graphql(query)
enum = ::Types::MergeRequestReviewStateEnum.values['REVIEWED']
enum = ::Types::MergeRequestReviewStateEnum.values['APPROVED']
expect(interaction_data).to contain_exactly a_hash_including(
'canMerge' => true,
'canUpdate' => true,
'reviewState' => enum.graphql_name,
'reviewed' => true,
'reviewed' => false,
'approved' => true
)
end

View File

@ -100,7 +100,7 @@ RSpec.describe API::MergeRequestApprovals, feature_category: :source_code_manage
MergeRequests::UpdateReviewerStateService,
project: project, current_user: unapprover
) do |service|
expect(service).to receive(:execute).with(merge_request, "unreviewed")
expect(service).to receive(:execute).with(merge_request, :unapproved)
end
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unapprove", unapprover)

View File

@ -111,6 +111,12 @@ RSpec.describe MergeRequests::ApprovalService, feature_category: :code_review_wo
.with(current_user_id: user.id, merge_request_id: merge_request.id)
end
it 'changes reviewers state to unapproved' do
expect { service.execute(merge_request) }.to change {
merge_request.merge_request_reviewers.reload.all?(&:approved?)
}.from(false).to(true)
end
it_behaves_like 'triggers GraphQL subscription mergeRequestMergeStatusUpdated' do
let(:action) { service.execute(merge_request) }
end

View File

@ -6,7 +6,7 @@ RSpec.describe MergeRequests::RemoveApprovalService, feature_category: :code_rev
describe '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:merge_request) { create(:merge_request, source_project: project, reviewers: [user]) }
let!(:existing_approval) { create(:approval, merge_request: merge_request) }
subject(:service) { described_class.new(project: project, current_user: user) }
@ -65,6 +65,12 @@ RSpec.describe MergeRequests::RemoveApprovalService, feature_category: :code_rev
expect { execute! }.to change { merge_request.approvals.size }.from(2).to(1)
end
it 'changes reviewers state to unapproved' do
expect { execute! }.to change {
merge_request.merge_request_reviewers.reload.all?(&:unapproved?)
}.from(false).to(true)
end
it 'creates an unapproval note, triggers a web hook, and sends a notification' do
expect(service).to receive(:execute_hooks).with(merge_request, 'unapproved')
expect(SystemNoteService).to receive(:unapprove_mr)

View File

@ -2779,7 +2779,7 @@ RSpec.describe QuickActions::InterpretService, feature_category: :team_planning
MergeRequests::UpdateReviewerStateService,
project: project, current_user: current_user
) do |service|
expect(service).to receive(:execute).with(merge_request, "unreviewed")
expect(service).to receive(:execute).with(merge_request, :unapproved)
end
service.execute(content, merge_request)

View File

@ -54,13 +54,17 @@ end
RSpec.shared_examples 'does not show New Snippet button' do
specify do
expect(page).to have_link(text: "$#{snippet.id}")
expect(page).not_to have_selector('[data-testid="snippets-more-actions-dropdown"]')
expect(page).not_to have_link('New snippet')
end
end
RSpec.shared_examples 'does show New Snippet button' do
specify do
find_by_testid('snippets-more-actions-dropdown-toggle').click
expect(page).to have_link(text: "$#{snippet.id}")
expect(page).to have_selector('[data-testid="snippets-more-actions-dropdown"]')
expect(page).to have_link('New snippet')
end
end

View File

@ -1321,10 +1321,10 @@
stylelint-declaration-strict-value "1.10.4"
stylelint-scss "6.0.0"
"@gitlab/svgs@3.91.0":
version "3.91.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.91.0.tgz#e99bc5467318cc8c156e02c574f5e65941b767f0"
integrity sha512-ozINZKyUlu5UQlP++9SntWQDqsgDSYawTPw+qhKQTdyn4Ut2NMsvSFyR5J2pax5IWu+SpLrn3WuPgNybK+4LqQ==
"@gitlab/svgs@3.93.0":
version "3.93.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.93.0.tgz#10a548931d7ece5e7f5d77b77c0aa76d02f7b408"
integrity sha512-RUMIf72M8trVZXRmUjH/54WJpMpV0tZTShSdV9ey1gtPgcpXEDg7HGprAzSfCZ6pMuhGXIasR3or7BHFM4DrSQ==
"@gitlab/ui@78.6.0":
version "78.6.0"