Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
725adcc1b7
commit
bb27269b9b
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}`"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ export default {
|
|||
:users="reviewers"
|
||||
:editable="canUpdate"
|
||||
:issuable-type="issuableType"
|
||||
class="gl-pt-2"
|
||||
@request-review="requestReview"
|
||||
@assign-self="reviewBySelf"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ module Integrations
|
|||
:password,
|
||||
:priority,
|
||||
:project_key,
|
||||
:project_keys,
|
||||
:project_name,
|
||||
:project_url,
|
||||
:recipients,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -57,3 +57,5 @@ module Types
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Types::MergeRequests::DetailedMergeStatusEnum.prepend_mod_with('Types::MergeRequests::DetailedMergeStatusEnum')
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ module MergeRequestReviewerState
|
|||
enum state: {
|
||||
unreviewed: 0,
|
||||
reviewed: 1,
|
||||
requested_changes: 2
|
||||
requested_changes: 2,
|
||||
approved: 3,
|
||||
unapproved: 4
|
||||
}
|
||||
|
||||
validates :state,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
@ -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 -->
|
||||
|
|
@ -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 -->
|
||||
|
|
@ -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. |
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
@ -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 -->
|
||||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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/)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 -->
|
||||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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. |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue