mirror of
https://github.com/gitlabhq/gitlabhq.git
synced 2025-07-21 23:56:17 +00:00
Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
@ -30,14 +30,10 @@ default:
|
||||
.default-ruby-variables: &default-ruby-variables
|
||||
RUBY_VERSION: "3.1"
|
||||
OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3_1"
|
||||
OMNIBUS_RUBY_VERSION: "3.1.5"
|
||||
CNG_RUBY_VERSION: "3.1.5"
|
||||
|
||||
.next-ruby-variables: &next-ruby-variables
|
||||
RUBY_VERSION: "3.2"
|
||||
OMNIBUS_GITLAB_CACHE_EDITION: "GITLAB_RUBY3_2"
|
||||
OMNIBUS_RUBY_VERSION: "3.2.4"
|
||||
CNG_RUBY_VERSION: "3.2.4"
|
||||
|
||||
.default-branch-pipeline-failure-variables: &default-branch-pipeline-failure-variables
|
||||
CREATE_RAILS_FLAKY_TEST_ISSUES: "true"
|
||||
|
@ -24,7 +24,7 @@ include:
|
||||
script:
|
||||
- 'ruby -r./scripts/trigger-build.rb -e "puts Trigger.variables_for_env_file(Trigger::CNG.new.variables)" > $BUILD_ENV'
|
||||
- echo "GITLAB_ASSETS_TAG=$(assets_image_tag)" >> $BUILD_ENV
|
||||
- echo "FULL_RUBY_VERSION=${CNG_RUBY_VERSION}" >> $BUILD_ENV
|
||||
- ruby -e 'puts "FULL_RUBY_VERSION=#{RUBY_VERSION}"' >> build.env
|
||||
- cat $BUILD_ENV
|
||||
artifacts:
|
||||
reports:
|
||||
|
@ -242,7 +242,7 @@ trigger-omnibus-env:
|
||||
echo "OMNIBUS_GITLAB_CACHE_EDITION=${OMNIBUS_GITLAB_CACHE_EDITION}" >> $BUILD_ENV
|
||||
for version_file in *_VERSION; do echo "$version_file=$(cat $version_file)" >> $BUILD_ENV; done
|
||||
echo "OMNIBUS_GITLAB_BUILD_ON_ALL_OS=${OMNIBUS_GITLAB_BUILD_ON_ALL_OS:-false}" >> $BUILD_ENV
|
||||
echo "FULL_RUBY_VERSION=${OMNIBUS_RUBY_VERSION}" >> $BUILD_ENV
|
||||
ruby -e 'puts "FULL_RUBY_VERSION=#{RUBY_VERSION}"' >> $BUILD_ENV
|
||||
echo "SHORT_RUBY_VERSION=${RUBY_VERSION}" >> $BUILD_ENV
|
||||
echo "GITLAB_ASSETS_TAG=$(assets_image_tag)" >> $BUILD_ENV
|
||||
echo "EE=$([[ $FOSS_ONLY == '1' ]] && echo 'false' || echo 'true')" >> $BUILD_ENV
|
||||
|
@ -1 +1 @@
|
||||
c222f55359efa198efd2c26d6d1c305a50619119
|
||||
501588fde1b1d35d9d4e441cf1455da9e49e5b9c
|
||||
|
@ -922,7 +922,7 @@ export default {
|
||||
:key="activeIssuable.iid"
|
||||
:work-item-iid="activeIssuable.iid"
|
||||
is-drawer
|
||||
class="gl-pt-0! work-item-drawer"
|
||||
class="gl-pt-0!"
|
||||
@work-item-updated="updateIssuablesCache"
|
||||
@work-item-emoji-updated="updateIssuableEmojis"
|
||||
@addChild="refetchIssuables"
|
||||
|
@ -208,7 +208,6 @@ export default {
|
||||
<gl-collapsible-listbox
|
||||
:id="inputId"
|
||||
ref="listbox"
|
||||
class="!gl-w-full"
|
||||
:multiple="multiSelect"
|
||||
:searchable="searchable"
|
||||
start-opened
|
||||
@ -219,6 +218,7 @@ export default {
|
||||
:header-text="headerText"
|
||||
:toggle-text="toggleText"
|
||||
:no-results-text="$options.i18n.noMatchingResults"
|
||||
positioning-strategy="fixed"
|
||||
:items="listItems"
|
||||
:selected="localSelectedItem"
|
||||
:reset-button-label="resetButton"
|
||||
|
@ -432,240 +432,242 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="work-item-view">
|
||||
<section v-if="updateError" class="flash-container flash-container-page sticky">
|
||||
<gl-alert class="gl-mb-3" variant="danger" @dismiss="updateError = undefined">
|
||||
{{ updateError }}
|
||||
</gl-alert>
|
||||
</section>
|
||||
<section :class="workItemBodyClass">
|
||||
<work-item-loading v-if="workItemLoading" />
|
||||
<template v-else>
|
||||
<div class="gl-sm-display-none! gl-display-flex">
|
||||
<gl-button
|
||||
v-if="isModal"
|
||||
class="gl-ml-auto"
|
||||
category="tertiary"
|
||||
data-testid="work-item-close"
|
||||
icon="close"
|
||||
:aria-label="__('Close')"
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="gl-display-block gl-sm-display-flex! gl-align-items-flex-start gl-flex-direction-row gl-gap-3 gl-pt-3"
|
||||
>
|
||||
<work-item-ancestors v-if="parentWorkItem" :work-item="workItem" class="gl-mb-1" />
|
||||
<div v-if="!error" :class="titleClassHeader" data-testid="work-item-type">
|
||||
<div>
|
||||
<work-item-sticky-header
|
||||
v-if="showIntersectionObserver"
|
||||
:current-user-todos="currentUserTodos"
|
||||
:show-work-item-current-user-todos="showWorkItemCurrentUserTodos"
|
||||
:parent-work-item-confidentiality="parentWorkItemConfidentiality"
|
||||
:update-in-progress="updateInProgress"
|
||||
:full-path="fullPath"
|
||||
:is-modal="isModal"
|
||||
:work-item="workItem"
|
||||
:is-sticky-header-showing="isStickyHeaderShowing"
|
||||
:work-item-notifications-subscribed="workItemNotificationsSubscribed"
|
||||
@hideStickyHeader="hideStickyHeader"
|
||||
@showStickyHeader="showStickyHeader"
|
||||
@deleteWorkItem="$emit('deleteWorkItem', { workItemType, workItemId: workItem.id })"
|
||||
@toggleWorkItemConfidentiality="toggleConfidentiality"
|
||||
@error="updateError = $event"
|
||||
@promotedToObjective="$emit('promotedToObjective', workItemIid)"
|
||||
@toggleEditMode="enableEditMode"
|
||||
/>
|
||||
<section class="work-item-view">
|
||||
<section v-if="updateError" class="flash-container flash-container-page sticky">
|
||||
<gl-alert class="gl-mb-3" variant="danger" @dismiss="updateError = undefined">
|
||||
{{ updateError }}
|
||||
</gl-alert>
|
||||
</section>
|
||||
<section :class="workItemBodyClass">
|
||||
<work-item-loading v-if="workItemLoading" />
|
||||
<template v-else>
|
||||
<div class="gl-sm-display-none! gl-display-flex">
|
||||
<gl-button
|
||||
v-if="isModal"
|
||||
class="gl-ml-auto"
|
||||
category="tertiary"
|
||||
data-testid="work-item-close"
|
||||
icon="close"
|
||||
:aria-label="__('Close')"
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="gl-display-block gl-sm-display-flex! gl-align-items-flex-start gl-flex-direction-row gl-gap-3 gl-pt-3"
|
||||
>
|
||||
<work-item-ancestors v-if="parentWorkItem" :work-item="workItem" class="gl-mb-1" />
|
||||
<div v-if="!error" :class="titleClassHeader" data-testid="work-item-type">
|
||||
<work-item-title
|
||||
v-if="workItem.title"
|
||||
ref="title"
|
||||
:is-editing="editMode"
|
||||
:title="workItem.title"
|
||||
@updateWorkItem="updateWorkItem"
|
||||
@updateDraft="updateDraft('title', $event)"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
</div>
|
||||
<div class="gl-display-flex gl-align-self-start gl-ml-auto gl-gap-3">
|
||||
<gl-button
|
||||
v-if="shouldShowEditButton"
|
||||
category="secondary"
|
||||
data-testid="work-item-edit-form-button"
|
||||
class="shortcut-edit-wi-description"
|
||||
@click="enableEditMode"
|
||||
>
|
||||
{{ __('Edit') }}
|
||||
</gl-button>
|
||||
<work-item-todos
|
||||
v-if="showWorkItemCurrentUserTodos"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItemIid"
|
||||
:work-item-fullpath="fullPath"
|
||||
:current-user-todos="currentUserTodos"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
<work-item-notifications-widget
|
||||
v-if="newTodoAndNotificationsEnabled"
|
||||
:full-path="fullPath"
|
||||
:work-item-id="workItem.id"
|
||||
:subscribed-to-notifications="workItemNotificationsSubscribed"
|
||||
:can-update="canUpdate"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
<work-item-actions
|
||||
:full-path="fullPath"
|
||||
:work-item-id="workItem.id"
|
||||
:hide-subscribe="newTodoAndNotificationsEnabled"
|
||||
:subscribed-to-notifications="workItemNotificationsSubscribed"
|
||||
:work-item-type="workItemType"
|
||||
:work-item-type-id="workItemTypeId"
|
||||
:work-item-iid="workItemIid"
|
||||
:can-delete="canDelete"
|
||||
:can-update="canUpdate"
|
||||
:is-confidential="workItem.confidential"
|
||||
:is-discussion-locked="isDiscussionLocked"
|
||||
:is-parent-confidential="parentWorkItemConfidentiality"
|
||||
:work-item-reference="workItem.reference"
|
||||
:work-item-create-note-email="workItem.createNoteEmail"
|
||||
:is-modal="isModal"
|
||||
:work-item-state="workItem.state"
|
||||
:has-children="hasChildren"
|
||||
@deleteWorkItem="$emit('deleteWorkItem', { workItemType, workItemId: workItem.id })"
|
||||
@toggleWorkItemConfidentiality="toggleConfidentiality"
|
||||
@error="updateError = $event"
|
||||
@promotedToObjective="$emit('promotedToObjective', workItemIid)"
|
||||
/>
|
||||
</div>
|
||||
<gl-button
|
||||
v-if="isModal"
|
||||
class="gl-display-none gl-sm-display-block!"
|
||||
category="tertiary"
|
||||
data-testid="work-item-close"
|
||||
icon="close"
|
||||
:aria-label="__('Close')"
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<work-item-title
|
||||
v-if="workItem.title"
|
||||
v-if="workItem.title && parentWorkItem"
|
||||
ref="title"
|
||||
:is-editing="editMode"
|
||||
:class="titleClassComponent"
|
||||
:title="workItem.title"
|
||||
@error="updateError = $event"
|
||||
@updateWorkItem="updateWorkItem"
|
||||
@updateDraft="updateDraft('title', $event)"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
<work-item-created-updated
|
||||
v-if="!editMode"
|
||||
:full-path="fullPath"
|
||||
:work-item-iid="workItemIid"
|
||||
:update-in-progress="updateInProgress"
|
||||
/>
|
||||
</div>
|
||||
<div class="gl-display-flex gl-align-self-start gl-ml-auto gl-gap-3">
|
||||
<gl-button
|
||||
v-if="shouldShowEditButton"
|
||||
category="secondary"
|
||||
data-testid="work-item-edit-form-button"
|
||||
class="shortcut-edit-wi-description"
|
||||
@click="enableEditMode"
|
||||
<div data-testid="work-item-overview" class="work-item-overview">
|
||||
<section>
|
||||
<work-item-description
|
||||
v-if="hasDescriptionWidget"
|
||||
:edit-mode="editMode"
|
||||
:full-path="fullPath"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItem.iid"
|
||||
:update-in-progress="updateInProgress"
|
||||
@updateWorkItem="updateWorkItem"
|
||||
@updateDraft="updateDraft('description', $event)"
|
||||
@cancelEditing="cancelEditing"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
<work-item-award-emoji
|
||||
v-if="workItemAwardEmoji"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-fullpath="fullPath"
|
||||
:award-emoji="workItemAwardEmoji.awardEmoji"
|
||||
:work-item-iid="workItemIid"
|
||||
@error="updateError = $event"
|
||||
@emoji-updated="$emit('work-item-emoji-updated', $event)"
|
||||
/>
|
||||
<design-widget v-if="!isDrawer && hasDesignWidget" :work-item-id="workItem.id" />
|
||||
</section>
|
||||
<aside
|
||||
data-testid="work-item-overview-right-sidebar"
|
||||
class="work-item-overview-right-sidebar"
|
||||
:class="{ 'is-modal': isModal }"
|
||||
>
|
||||
{{ __('Edit') }}
|
||||
</gl-button>
|
||||
<work-item-todos
|
||||
v-if="showWorkItemCurrentUserTodos"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItemIid"
|
||||
:work-item-fullpath="fullPath"
|
||||
:current-user-todos="currentUserTodos"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
<work-item-notifications-widget
|
||||
v-if="newTodoAndNotificationsEnabled"
|
||||
<work-item-attributes-wrapper
|
||||
:full-path="fullPath"
|
||||
:work-item="workItem"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<work-item-tree
|
||||
v-if="showWorkItemTree"
|
||||
:full-path="fullPath"
|
||||
:work-item-id="workItem.id"
|
||||
:subscribed-to-notifications="workItemNotificationsSubscribed"
|
||||
:can-update="canUpdate"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
<work-item-actions
|
||||
:full-path="fullPath"
|
||||
:work-item-id="workItem.id"
|
||||
:hide-subscribe="newTodoAndNotificationsEnabled"
|
||||
:subscribed-to-notifications="workItemNotificationsSubscribed"
|
||||
:work-item-type="workItemType"
|
||||
:work-item-type-id="workItemTypeId"
|
||||
:parent-work-item-type="workItem.workItemType.name"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItemIid"
|
||||
:can-delete="canDelete"
|
||||
:children="children"
|
||||
:can-update="canUpdate"
|
||||
:is-confidential="workItem.confidential"
|
||||
:is-discussion-locked="isDiscussionLocked"
|
||||
:is-parent-confidential="parentWorkItemConfidentiality"
|
||||
:work-item-reference="workItem.reference"
|
||||
:work-item-create-note-email="workItem.createNoteEmail"
|
||||
:is-modal="isModal"
|
||||
:work-item-state="workItem.state"
|
||||
:has-children="hasChildren"
|
||||
@deleteWorkItem="$emit('deleteWorkItem', { workItemType, workItemId: workItem.id })"
|
||||
@toggleWorkItemConfidentiality="toggleConfidentiality"
|
||||
@error="updateError = $event"
|
||||
@promotedToObjective="$emit('promotedToObjective', workItemIid)"
|
||||
:confidential="workItem.confidential"
|
||||
@show-modal="openInModal"
|
||||
@addChild="$emit('addChild')"
|
||||
/>
|
||||
</div>
|
||||
<gl-button
|
||||
v-if="isModal"
|
||||
class="gl-display-none gl-sm-display-block!"
|
||||
category="tertiary"
|
||||
data-testid="work-item-close"
|
||||
icon="close"
|
||||
:aria-label="__('Close')"
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<work-item-title
|
||||
v-if="workItem.title && parentWorkItem"
|
||||
ref="title"
|
||||
:is-editing="editMode"
|
||||
:class="titleClassComponent"
|
||||
:title="workItem.title"
|
||||
@error="updateError = $event"
|
||||
@updateWorkItem="updateWorkItem"
|
||||
@updateDraft="updateDraft('title', $event)"
|
||||
/>
|
||||
<work-item-created-updated
|
||||
v-if="!editMode"
|
||||
:full-path="fullPath"
|
||||
:work-item-iid="workItemIid"
|
||||
:update-in-progress="updateInProgress"
|
||||
/>
|
||||
</div>
|
||||
<work-item-sticky-header
|
||||
v-if="showIntersectionObserver"
|
||||
:current-user-todos="currentUserTodos"
|
||||
:show-work-item-current-user-todos="showWorkItemCurrentUserTodos"
|
||||
:parent-work-item-confidentiality="parentWorkItemConfidentiality"
|
||||
:update-in-progress="updateInProgress"
|
||||
:full-path="fullPath"
|
||||
:is-modal="isModal"
|
||||
:work-item="workItem"
|
||||
:is-sticky-header-showing="isStickyHeaderShowing"
|
||||
:work-item-notifications-subscribed="workItemNotificationsSubscribed"
|
||||
@hideStickyHeader="hideStickyHeader"
|
||||
@showStickyHeader="showStickyHeader"
|
||||
@deleteWorkItem="$emit('deleteWorkItem', { workItemType, workItemId: workItem.id })"
|
||||
@toggleWorkItemConfidentiality="toggleConfidentiality"
|
||||
@error="updateError = $event"
|
||||
@promotedToObjective="$emit('promotedToObjective', workItemIid)"
|
||||
@toggleEditMode="enableEditMode"
|
||||
/>
|
||||
<div data-testid="work-item-overview" class="work-item-overview">
|
||||
<section>
|
||||
<work-item-description
|
||||
v-if="hasDescriptionWidget"
|
||||
:edit-mode="editMode"
|
||||
<work-item-relationships
|
||||
v-if="workItemLinkedItems"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItemIid"
|
||||
:work-item-full-path="fullPath"
|
||||
:work-item-type="workItem.workItemType.name"
|
||||
@showModal="openInModal"
|
||||
/>
|
||||
<work-item-notes
|
||||
v-if="workItemNotes"
|
||||
:full-path="fullPath"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItem.iid"
|
||||
:update-in-progress="updateInProgress"
|
||||
@updateWorkItem="updateWorkItem"
|
||||
@updateDraft="updateDraft('description', $event)"
|
||||
@cancelEditing="cancelEditing"
|
||||
:work-item-type="workItemType"
|
||||
:is-modal="isModal"
|
||||
:assignees="workItemAssignees && workItemAssignees.assignees.nodes"
|
||||
:can-set-work-item-metadata="canAssignUnassignUser"
|
||||
:report-abuse-path="reportAbusePath"
|
||||
:is-discussion-locked="isDiscussionLocked"
|
||||
:is-work-item-confidential="workItem.confidential"
|
||||
class="gl-pt-5"
|
||||
:use-h2="!isModal"
|
||||
@error="updateError = $event"
|
||||
@has-notes="updateHasNotes"
|
||||
@openReportAbuse="openReportAbuseDrawer"
|
||||
/>
|
||||
<work-item-award-emoji
|
||||
v-if="workItemAwardEmoji"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-fullpath="fullPath"
|
||||
:award-emoji="workItemAwardEmoji.awardEmoji"
|
||||
:work-item-iid="workItemIid"
|
||||
@error="updateError = $event"
|
||||
@emoji-updated="$emit('work-item-emoji-updated', $event)"
|
||||
<gl-empty-state
|
||||
v-if="error"
|
||||
:title="$options.i18n.fetchErrorTitle"
|
||||
:description="error"
|
||||
:svg-path="noAccessSvgPath"
|
||||
:svg-height="null"
|
||||
/>
|
||||
<design-widget v-if="!isDrawer && hasDesignWidget" :work-item-id="workItem.id" />
|
||||
</section>
|
||||
<aside
|
||||
data-testid="work-item-overview-right-sidebar"
|
||||
class="work-item-overview-right-sidebar"
|
||||
:class="{ 'is-modal': isModal }"
|
||||
>
|
||||
<work-item-attributes-wrapper
|
||||
:full-path="fullPath"
|
||||
:work-item="workItem"
|
||||
@error="updateError = $event"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<work-item-tree
|
||||
v-if="showWorkItemTree"
|
||||
:full-path="fullPath"
|
||||
:work-item-type="workItemType"
|
||||
:parent-work-item-type="workItem.workItemType.name"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItemIid"
|
||||
:children="children"
|
||||
:can-update="canUpdate"
|
||||
:confidential="workItem.confidential"
|
||||
@show-modal="openInModal"
|
||||
@addChild="$emit('addChild')"
|
||||
/>
|
||||
<work-item-relationships
|
||||
v-if="workItemLinkedItems"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItemIid"
|
||||
:work-item-full-path="fullPath"
|
||||
:work-item-type="workItem.workItemType.name"
|
||||
@showModal="openInModal"
|
||||
/>
|
||||
<work-item-notes
|
||||
v-if="workItemNotes"
|
||||
:full-path="fullPath"
|
||||
:work-item-id="workItem.id"
|
||||
:work-item-iid="workItem.iid"
|
||||
:work-item-type="workItemType"
|
||||
:is-modal="isModal"
|
||||
:assignees="workItemAssignees && workItemAssignees.assignees.nodes"
|
||||
:can-set-work-item-metadata="canAssignUnassignUser"
|
||||
:report-abuse-path="reportAbusePath"
|
||||
:is-discussion-locked="isDiscussionLocked"
|
||||
:is-work-item-confidential="workItem.confidential"
|
||||
class="gl-pt-5"
|
||||
:use-h2="!isModal"
|
||||
@error="updateError = $event"
|
||||
@has-notes="updateHasNotes"
|
||||
@openReportAbuse="openReportAbuseDrawer"
|
||||
/>
|
||||
<gl-empty-state
|
||||
v-if="error"
|
||||
:title="$options.i18n.fetchErrorTitle"
|
||||
:description="error"
|
||||
:svg-path="noAccessSvgPath"
|
||||
:svg-height="null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<work-item-detail-modal
|
||||
v-if="!isModal"
|
||||
ref="modal"
|
||||
:work-item-id="modalWorkItemId"
|
||||
:work-item-iid="modalWorkItemIid"
|
||||
:work-item-full-path="modalWorkItemNamespaceFullPath"
|
||||
:show="true"
|
||||
@close="updateUrl"
|
||||
@openReportAbuse="toggleReportAbuseDrawer(true, $event)"
|
||||
/>
|
||||
<abuse-category-selector
|
||||
v-if="isReportDrawerOpen"
|
||||
:reported-user-id="reportedUserId"
|
||||
:reported-from-url="reportedUrl"
|
||||
:show-drawer="true"
|
||||
@close-drawer="toggleReportAbuseDrawer(false)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<work-item-detail-modal
|
||||
v-if="!isModal"
|
||||
ref="modal"
|
||||
:work-item-id="modalWorkItemId"
|
||||
:work-item-iid="modalWorkItemIid"
|
||||
:work-item-full-path="modalWorkItemNamespaceFullPath"
|
||||
:show="true"
|
||||
@close="updateUrl"
|
||||
@openReportAbuse="toggleReportAbuseDrawer(true, $event)"
|
||||
/>
|
||||
<abuse-category-selector
|
||||
v-if="isReportDrawerOpen"
|
||||
:reported-user-id="reportedUserId"
|
||||
:reported-from-url="reportedUrl"
|
||||
:show-drawer="true"
|
||||
@close-drawer="toggleReportAbuseDrawer(false)"
|
||||
/>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -242,42 +242,39 @@ export default {
|
||||
>{{ __('Apply') }}</gl-button
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<!-- wrapper for the form input so the borders fit inside the sidebar -->
|
||||
<div class="gl-pr-2 gl-relative">
|
||||
<gl-collapsible-listbox
|
||||
id="$options.inputId"
|
||||
ref="input"
|
||||
class="gl-display-block"
|
||||
data-testid="work-item-parent-listbox"
|
||||
block
|
||||
searchable
|
||||
start-opened
|
||||
is-check-centered
|
||||
category="primary"
|
||||
fluid-width
|
||||
:searching="isLoading"
|
||||
:header-text="$options.i18n.assignParentLabel"
|
||||
:no-results-text="$options.i18n.noMatchingResults"
|
||||
:loading="updateInProgress"
|
||||
:items="workItems"
|
||||
:toggle-text="listboxText"
|
||||
:selected="localSelectedItem"
|
||||
:reset-button-label="$options.i18n.unAssign"
|
||||
@reset="unassignParent"
|
||||
@search="debouncedSearchKeyUpdate"
|
||||
@select="handleItemClick"
|
||||
@shown="onListboxShown"
|
||||
@hidden="onListboxHide"
|
||||
>
|
||||
<template #list-item="{ item }">
|
||||
<div @click="handleItemClick(item.value, $event)">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</template>
|
||||
</gl-collapsible-listbox>
|
||||
</div>
|
||||
</div>
|
||||
<gl-collapsible-listbox
|
||||
id="$options.inputId"
|
||||
ref="input"
|
||||
class="gl-display-block"
|
||||
data-testid="work-item-parent-listbox"
|
||||
block
|
||||
searchable
|
||||
start-opened
|
||||
is-check-centered
|
||||
category="primary"
|
||||
fluid-width
|
||||
toggle-class="work-item-sidebar-dropdown-toggle"
|
||||
positioning-strategy="fixed"
|
||||
:searching="isLoading"
|
||||
:header-text="$options.i18n.assignParentLabel"
|
||||
:no-results-text="$options.i18n.noMatchingResults"
|
||||
:loading="updateInProgress"
|
||||
:items="workItems"
|
||||
:toggle-text="listboxText"
|
||||
:selected="localSelectedItem"
|
||||
:reset-button-label="$options.i18n.unAssign"
|
||||
@reset="unassignParent"
|
||||
@search="debouncedSearchKeyUpdate"
|
||||
@select="handleItemClick"
|
||||
@shown="onListboxShown"
|
||||
@hidden="onListboxHide"
|
||||
>
|
||||
<template #list-item="{ item }">
|
||||
<div @click="handleItemClick(item.value, $event)">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</template>
|
||||
</gl-collapsible-listbox>
|
||||
</gl-form>
|
||||
<template v-else-if="hasParent">
|
||||
<gl-link
|
||||
|
@ -143,6 +143,24 @@ $work-item-overview-gap-width: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.work-item-view {
|
||||
container-name: work-item-view;
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
@container work-item-view (max-width: #{calc($breakpoint-md - 1px)}) {
|
||||
.work-item-overview {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.work-item-attributes-wrapper {
|
||||
position: static !important;
|
||||
overflow-y: initial !important;
|
||||
margin-bottom: 0 !important;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
.work-item-overview {
|
||||
@include media-breakpoint-up(md) {
|
||||
display: grid;
|
||||
@ -156,8 +174,26 @@ $work-item-overview-gap-width: 2rem;
|
||||
|
||||
.work-item-overview-right-sidebar {
|
||||
margin-top: $gl-spacing-scale-5;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
@container work-item-view (min-width: #{$breakpoint-md}) {
|
||||
.work-item-attributes-wrapper {
|
||||
top: calc(#{$calc-application-header-height} + #{$work-item-sticky-header-height});
|
||||
height: calc(#{$calc-application-viewport-height} - #{$work-item-sticky-header-height});
|
||||
margin-bottom: calc(#{$content-wrapper-padding} * -1);
|
||||
padding-inline: 0.5rem;
|
||||
position: sticky;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.work-item-date-picker {
|
||||
max-width: 175px;
|
||||
}
|
||||
}
|
||||
|
||||
@container work-item-view (min-width: #{$breakpoint-md}) {
|
||||
.work-item-overview-right-sidebar {
|
||||
margin-top: 0;
|
||||
grid-row-start: 1;
|
||||
grid-column-start: 2;
|
||||
@ -171,24 +207,6 @@ $work-item-overview-gap-width: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.work-item-attributes-wrapper {
|
||||
.work-item-overview & {
|
||||
@include media-breakpoint-up(md) {
|
||||
top: calc(#{$calc-application-header-height} + #{$work-item-sticky-header-height});
|
||||
height: calc(#{$calc-application-viewport-height} - #{$work-item-sticky-header-height});
|
||||
margin-bottom: calc(#{$content-wrapper-padding} * -1);
|
||||
padding-inline: 0.5rem;
|
||||
position: sticky;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.work-item-date-picker {
|
||||
max-width: 175px;
|
||||
}
|
||||
}
|
||||
|
||||
.work-item-field-label {
|
||||
.work-item-overview & {
|
||||
max-width: 30%;
|
||||
@ -380,7 +398,8 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
||||
}
|
||||
|
||||
.work-item-sidebar-dropdown-toggle ~ .gl-new-dropdown-panel {
|
||||
width: 95% !important;
|
||||
width: 100% !important;
|
||||
max-width: 19rem !important;
|
||||
}
|
||||
|
||||
.container-limited {
|
||||
@ -430,15 +449,4 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.work-item-view.work-item-drawer {
|
||||
container-name: view;
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
@container view (max-width: 40vw) {
|
||||
.work-item-overview {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
@ -288,6 +288,14 @@ module MergeRequests
|
||||
.new(project: merge_request.project, current_user: user)
|
||||
.execute(merge_request, state)
|
||||
end
|
||||
|
||||
def abort_auto_merge_with_todo(merge_request, reason)
|
||||
response = abort_auto_merge(merge_request, reason)
|
||||
response = ServiceResponse.new(**response)
|
||||
return unless response.success?
|
||||
|
||||
todo_service.merge_request_became_unmergeable(merge_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,7 +103,8 @@ module MergeRequests
|
||||
|
||||
def after_merge
|
||||
log_info("Post merge started on JID #{merge_jid} with state #{state}")
|
||||
MergeRequests::PostMergeService.new(project: project, current_user: current_user).execute(merge_request)
|
||||
MergeRequests::PostMergeService.new(project: project, current_user: current_user, params: { delete_source_branch:
|
||||
delete_source_branch? }).execute(merge_request)
|
||||
log_info("Post merge finished on JID #{merge_jid} with state #{state}")
|
||||
|
||||
if delete_source_branch?
|
||||
|
@ -33,6 +33,7 @@ module MergeRequests
|
||||
cleanup_environments(merge_request)
|
||||
cleanup_refs(merge_request)
|
||||
deactivate_pages_deployments(merge_request)
|
||||
cancel_auto_merges_targeting_source_branch(merge_request)
|
||||
|
||||
execute_hooks(merge_request, 'merge')
|
||||
end
|
||||
@ -87,6 +88,20 @@ module MergeRequests
|
||||
merge_request_metrics_service(merge_request).merge(merge_event)
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_auto_merges_targeting_source_branch(merge_request)
|
||||
return unless Feature.enabled?(:merge_when_checks_pass, merge_request.project)
|
||||
return unless params[:delete_source_branch]
|
||||
|
||||
merge_request.source_project
|
||||
.merge_requests
|
||||
.by_target_branch(merge_request.source_branch)
|
||||
.with_auto_merge_enabled.each do |targetting_merge_request|
|
||||
if targetting_merge_request.auto_merge_strategy == ::AutoMergeService::STRATEGY_MERGE_WHEN_CHECKS_PASS
|
||||
abort_auto_merge_with_todo(targetting_merge_request, "target branch was merged in #{merge_request.iid}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -211,14 +211,6 @@ module MergeRequests
|
||||
end
|
||||
end
|
||||
|
||||
def abort_auto_merge_with_todo(merge_request, reason)
|
||||
response = abort_auto_merge(merge_request, reason)
|
||||
response = ServiceResponse.new(**response)
|
||||
return unless response.success?
|
||||
|
||||
todo_service.merge_request_became_unmergeable(merge_request)
|
||||
end
|
||||
|
||||
def merge_requests_with_auto_merge_enabled_to(target_branch)
|
||||
@project
|
||||
.merge_requests
|
||||
|
@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RemoveIndexOnSbomOccurrencesComponentVersionIdAndTraversalIds < Gitlab::Database::Migration[2.2]
|
||||
INDEX_NAME = 'idx_sbom_occurrences_on_component_version_id_and_traversal_ids'
|
||||
|
||||
milestone '17.1'
|
||||
|
||||
def up
|
||||
prepare_async_index_removal :sbom_occurrences, [:component_version_id, :traversal_ids], name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
unprepare_async_index :sbom_occurrences, [:component_version_id, :traversal_ids], name: INDEX_NAME
|
||||
end
|
||||
end
|
@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RemoveIndexOnSbomOccurrencesForAggregations < Gitlab::Database::Migration[2.2]
|
||||
INDEX_NAME = 'index_sbom_occurrences_for_aggregations'
|
||||
|
||||
milestone '17.1'
|
||||
|
||||
def up
|
||||
prepare_async_index_removal :sbom_occurrences, [:traversal_ids, :component_id, :component_version_id],
|
||||
name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
unprepare_async_index :sbom_occurrences, [:traversal_ids, :component_id, :component_version_id], name: INDEX_NAME
|
||||
end
|
||||
end
|
1
db/schema_migrations/20240522164424
Normal file
1
db/schema_migrations/20240522164424
Normal file
@ -0,0 +1 @@
|
||||
460eff695c8538e75979dbe9ddc65d5d1d6753aeab1c0c4e65bd86e0c584ead3
|
1
db/schema_migrations/20240522164922
Normal file
1
db/schema_migrations/20240522164922
Normal file
@ -0,0 +1 @@
|
||||
19958f5614dd5501914168eadbeeb7c23de8bcebb9ca5b27d72764b2fab3abc8
|
@ -24,7 +24,9 @@ with the `--cache-from` argument must be pulled
|
||||
|
||||
## Docker caching example
|
||||
|
||||
This example `.gitlab-ci.yml` file shows how to use Docker caching:
|
||||
This example `.gitlab-ci.yml` file shows how to use Docker caching with
|
||||
the `inline` cache backend with the default `docker build` command. For
|
||||
more advanced caching options, see the [`docker buildx build` command and its cache options](https://docs.docker.com/build/cache/backends/).
|
||||
|
||||
```yaml
|
||||
default:
|
||||
@ -43,7 +45,7 @@ build:
|
||||
stage: build
|
||||
script:
|
||||
- docker pull $CI_REGISTRY_IMAGE:latest || true
|
||||
- docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest .
|
||||
- docker build --build-arg BUILDKIT_INLINE_CACHE=1 --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --tag $CI_REGISTRY_IMAGE:latest .
|
||||
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
||||
- docker push $CI_REGISTRY_IMAGE:latest
|
||||
```
|
||||
@ -54,6 +56,8 @@ In the `script` section for the `build` job:
|
||||
used as a cache for the `docker build` command.
|
||||
1. The second command builds a Docker image by using the pulled image as a
|
||||
cache (see the `--cache-from $CI_REGISTRY_IMAGE:latest` argument) if
|
||||
available, and tags it.
|
||||
available, and tags it. The `--build-arg BUILDKIT_INLINE_CACHE=1` tells
|
||||
Docker to use [inline caching](https://docs.docker.com/build/cache/backends/inline/),
|
||||
which embeds the build cache into the image itself.
|
||||
1. The last two commands push the tagged Docker images to the container registry
|
||||
so that they can also be used as cache for subsequent builds.
|
||||
|
@ -394,10 +394,11 @@ The following package managers use lockfiles that GitLab analyzers are capable o
|
||||
</tr>
|
||||
<tr>
|
||||
<td>pnpm</td>
|
||||
<td>v5, v6</td>
|
||||
<td>v5, v6, v9</td>
|
||||
<td>
|
||||
<a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-pnpm/default/pnpm-lock.yaml#L1">7.x</a>,
|
||||
<a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/scanner/parser/pnpm/fixtures/v6/simple/pnpm-lock.yaml#L1">8.x</a>
|
||||
<a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/scanner/parser/pnpm/fixtures/v9/simple/pnpm-lock.yaml#L1">9.x</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -1242,6 +1243,10 @@ project for an example of how this can be done.
|
||||
|
||||
We recommend that you use the most recent version of all containers, and the most recent supported version of all package managers and languages. Using previous versions carries an increased security risk because unsupported versions may no longer benefit from active security reporting and backporting of security fixes.
|
||||
|
||||
### Gradle projects
|
||||
|
||||
Do not override the `reports.html.destination` or `reports.html.outputLocation` properties when generating an HTML dependency report for Gradle projects. Doing so prevents Dependency Scanning from functioning correctly.
|
||||
|
||||
### Python projects
|
||||
|
||||
Extra care needs to be taken when using the [`PIP_EXTRA_INDEX_URL`](https://pipenv.pypa.io/en/latest/cli/#envvar-PIP_EXTRA_INDEX_URL)
|
||||
|
@ -187,10 +187,6 @@ gemnasium-python-dependency_scanning:
|
||||
This error can occur when the automatically generated `CI_JOB_TOKEN` starts with a hyphen (`-`).
|
||||
To avoid this error, follow [Poetry's configuration advice](https://python-poetry.org/docs/repositories/#configuring-credentials).
|
||||
|
||||
## Error: Project has `<number>` unresolved dependencies
|
||||
|
||||
The error message `Project has <number> unresolved dependencies` indicates a dependency resolution problem caused by your `gradle.build` or `gradle.build.kts` file. In the current release, `gemnasium-maven` cannot continue processing when an unresolved dependency is encountered. However, there is an [open epic](https://gitlab.com/groups/gitlab-org/-/epics/12361) to allow `gemnasium-maven` to recover from unresolved dependency errors and produce a dependency graph. Until this epic has been resolved, consult the [Gradle dependency resolution documentation](https://docs.gradle.org/current/userguide/dependency_resolution.html) for details on how to fix your `gradle.build` file.
|
||||
|
||||
## Setting build constraints when scanning Go projects
|
||||
|
||||
Dependency scanning runs within a `linux/amd64` container. As a result, the build list generated
|
||||
|
@ -570,6 +570,157 @@ Access permissions for value stream analytics depend on the project type.
|
||||
| Internal | Any authenticated user can access. |
|
||||
| Private | Any user with at least the Guest role can access. |
|
||||
|
||||
## Value Stream Analytics GraphQL API
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
|
||||
|
||||
> - Loading stage metrics through GraphQL [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/410327) in GitLab 17.0.
|
||||
|
||||
With the VSA GraphQL API, you can request metrics from your configured value streams and value stream stages. This can be useful if you want to export VSA data to an external system or for a report.
|
||||
|
||||
The following metrics are available:
|
||||
|
||||
- Number of completed items in the stage. The count is limited to a maximum of 10,000 items.
|
||||
- Median duration for the completed items in the stage.
|
||||
- Average duration for the completed items in the stage.
|
||||
|
||||
### Request the metrics
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role.
|
||||
|
||||
First, you must determine which value stream you want to use in the reporting.
|
||||
|
||||
To request the configured value streams for a group, run:
|
||||
|
||||
```graphql
|
||||
group(fullPath: "your-group-path") {
|
||||
valueStreams {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, to request metrics for a project, run:
|
||||
|
||||
```graphl
|
||||
project(fullPath: "your-group-path") {
|
||||
valueStreams {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To request metrics for stages of a value stream, run:
|
||||
|
||||
```graphql
|
||||
group(fullPath: "your-group-path") {
|
||||
valueStreams(id: "your-value-stream-id") {
|
||||
nodes {
|
||||
stages {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Depending how you want to consume the data, you can request metrics for one specific stage or all stages in your value stream.
|
||||
|
||||
NOTE:
|
||||
Requesting metrics for all stages might be too slow for some installations.
|
||||
The recommended approach is to request metrics stage by stage.
|
||||
|
||||
Requesting metrics for the stage:
|
||||
|
||||
```graphql
|
||||
group(fullPath: "your-group-path") {
|
||||
valueStreams(id: "your-value-stream-id") {
|
||||
nodes {
|
||||
stages(id: "your-stage-id") {
|
||||
id
|
||||
name
|
||||
metrics(timeframe: { start: "2024-03-01", end: "2024-03-31" }) {
|
||||
average {
|
||||
value
|
||||
unit
|
||||
}
|
||||
median {
|
||||
value
|
||||
unit
|
||||
}
|
||||
count {
|
||||
value
|
||||
unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
NOTE:
|
||||
You should always request metrics with a given time frame.
|
||||
The longest supported time frame is 180 days.
|
||||
|
||||
The `metrics` node supports additional filtering options:
|
||||
|
||||
- Assignee usernames
|
||||
- Author username
|
||||
- Label names
|
||||
- Milestone title
|
||||
|
||||
Example request with filters:
|
||||
|
||||
```graphql
|
||||
group(fullPath: "your-group-path") {
|
||||
valueStreams(id: "your-value-stream-id") {
|
||||
nodes {
|
||||
stages(id: "your-stage-id") {
|
||||
id
|
||||
name
|
||||
metrics(
|
||||
labelNames: ["backend"],
|
||||
milestoneTitle: "17.0",
|
||||
timeframe: { start: "2024-03-01", end: "2024-03-31" }
|
||||
) {
|
||||
average {
|
||||
value
|
||||
unit
|
||||
}
|
||||
median {
|
||||
value
|
||||
unit
|
||||
}
|
||||
count {
|
||||
value
|
||||
unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Best practices
|
||||
|
||||
- To get an accurate view of the current status, request metrics as close to the end of the time frame as possible.
|
||||
- For periodic reporting, you can create a script and use the [scheduled pipelines](../../../ci/pipelines/schedules.md) feature to export the data in a timely manner.
|
||||
- When invoking the API, you get the current data from the database. Over time, the same metrics might change due to changes in the underlying data in the database. For example, moving or removing a project from the group might affect group-level metrics.
|
||||
- Re-requesting the metrics for previous periods and comparing them to the previously collected metrics can show skews in the data, which can help in discovering and explaining changing trends.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### 100% CPU utilization by Sidekiq `cronjob:analytics_cycle_analytics`
|
||||
|
@ -7,7 +7,6 @@
|
||||
---
|
||||
agent_users_using_ci_tunnel-user: agent_users_using_ci_tunnel
|
||||
ci_template_included-project: ci_template_included
|
||||
code_suggestions_requested-user: code_suggestions_requested
|
||||
create_ci_build-user: create_ci_build
|
||||
create_ci_internal_pipeline-user: create_ci_internal_pipeline
|
||||
exclude_anonymised_users-user: exclude_anonymised_users
|
||||
|
@ -140,6 +140,15 @@ FactoryBot.define do
|
||||
merge_params { { sha: diff_head_sha } }
|
||||
end
|
||||
|
||||
trait :merge_when_checks_pass do
|
||||
auto_merge_enabled { true }
|
||||
auto_merge_strategy { AutoMergeService::STRATEGY_MERGE_WHEN_CHECKS_PASS }
|
||||
merge_user { author }
|
||||
merge_params do
|
||||
{ sha: diff_head_sha, 'auto_merge_strategy' => AutoMergeService::STRATEGY_MERGE_WHEN_CHECKS_PASS }
|
||||
end
|
||||
end
|
||||
|
||||
trait :remove_source_branch do
|
||||
merge_params do
|
||||
{ 'force_remove_source_branch' => '1' }
|
||||
|
@ -63,7 +63,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin
|
||||
end
|
||||
|
||||
it 'has event definitions for all events used in Internal Events metric definitions', :aggregate_failures do
|
||||
from_metric_definitions = Gitlab::Usage::MetricDefinition.definitions
|
||||
from_metric_definitions = Gitlab::Usage::MetricDefinition.not_removed
|
||||
.values
|
||||
.select { |m| m.attributes[:data_source] == 'internal_events' }
|
||||
.flat_map { |m| m.events&.keys }
|
||||
|
@ -8,8 +8,9 @@ RSpec.describe MergeRequests::PostMergeService, feature_category: :code_review_w
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:merge_request, reload: true) { create(:merge_request, assignees: [user]) }
|
||||
let_it_be(:project) { merge_request.project }
|
||||
let(:params) { {} }
|
||||
|
||||
subject { described_class.new(project: project, current_user: user).execute(merge_request) }
|
||||
subject { described_class.new(project: project, current_user: user, params: params).execute(merge_request) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
@ -167,6 +168,53 @@ RSpec.describe MergeRequests::PostMergeService, feature_category: :code_review_w
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are auto merge MRs with the branch as target' do
|
||||
context 'when merge_when_checks_pass is disabled and when source branch is to be deleted' do
|
||||
let(:params) { { delete_source_branch: true } }
|
||||
|
||||
it 'aborts no non MWCP auto merges' do
|
||||
mr_1 = create(:merge_request, :merge_when_pipeline_succeeds, target_branch: merge_request.source_branch,
|
||||
source_branch: "test", source_project: merge_request.project)
|
||||
mr_2 = create(:merge_request, :merge_when_pipeline_succeeds, target_branch: 'feature',
|
||||
source_branch: 'second', source_project: merge_request.project)
|
||||
|
||||
expect(merge_request.source_project.merge_requests.with_auto_merge_enabled).to contain_exactly(mr_1, mr_2)
|
||||
subject
|
||||
expect(merge_request.source_project.merge_requests.with_auto_merge_enabled).to contain_exactly(mr_1, mr_2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when source branch is not be deleted' do
|
||||
it 'aborts no non MWCP auto merges' do
|
||||
mr_1 = create(:merge_request, :merge_when_pipeline_succeeds, target_branch: merge_request.source_branch,
|
||||
source_branch: "test", source_project: merge_request.project)
|
||||
mr_2 = create(:merge_request, :merge_when_pipeline_succeeds, target_branch: 'feature',
|
||||
source_branch: 'second', source_project: merge_request.project)
|
||||
|
||||
expect(merge_request.source_project.merge_requests.with_auto_merge_enabled).to contain_exactly(mr_1, mr_2)
|
||||
subject
|
||||
expect(merge_request.source_project.merge_requests.with_auto_merge_enabled).to contain_exactly(mr_1, mr_2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when merge_when_checks_pass is disabled' do
|
||||
before do
|
||||
stub_feature_flags(merge_when_checks_pass: false)
|
||||
end
|
||||
|
||||
it 'aborts no non MWCP auto merges' do
|
||||
mr_1 = create(:merge_request, :merge_when_pipeline_succeeds, target_branch: merge_request.source_branch,
|
||||
source_branch: "test", source_project: merge_request.project)
|
||||
mr_2 = create(:merge_request, :merge_when_pipeline_succeeds, target_branch: 'feature',
|
||||
source_branch: 'second', source_project: merge_request.project)
|
||||
|
||||
expect(merge_request.source_project.merge_requests.with_auto_merge_enabled).to contain_exactly(mr_1, mr_2)
|
||||
subject
|
||||
expect(merge_request.source_project.merge_requests.with_auto_merge_enabled).to contain_exactly(mr_1, mr_2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when event source is given' do
|
||||
let(:source) { create(:merge_request, :simple, source_project: project) }
|
||||
|
||||
|
@ -5,7 +5,7 @@ go 1.21
|
||||
toolchain go1.21.9
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
|
||||
github.com/BurntSushi/toml v1.3.2
|
||||
github.com/alecthomas/chroma/v2 v2.13.0
|
||||
github.com/aws/aws-sdk-go v1.51.14
|
||||
@ -32,14 +32,14 @@ require (
|
||||
golang.org/x/net v0.22.0
|
||||
golang.org/x/oauth2 v0.18.0
|
||||
golang.org/x/tools v0.19.0
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
google.golang.org/grpc v1.64.0
|
||||
google.golang.org/protobuf v1.34.1
|
||||
honnef.co/go/tools v0.4.7
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.112.1 // indirect
|
||||
cloud.google.com/go/compute v1.25.0 // indirect
|
||||
cloud.google.com/go/compute v1.25.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.6 // indirect
|
||||
cloud.google.com/go/monitoring v1.18.0 // indirect
|
||||
@ -47,7 +47,7 @@ require (
|
||||
cloud.google.com/go/storage v1.39.1 // indirect
|
||||
cloud.google.com/go/trace v1.10.5 // indirect
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.14 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
@ -128,8 +128,8 @@ require (
|
||||
google.golang.org/api v0.169.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
@ -32,8 +32,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU=
|
||||
cloud.google.com/go/compute v1.25.0/go.mod h1:GR7F0ZPZH8EhChlMo9FkLd7eUTwEymjqQagxzilIxIE=
|
||||
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
|
||||
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
@ -60,16 +60,16 @@ cloud.google.com/go/trace v1.10.5/go.mod h1:9hjCV1nGBCtXbAE4YK7OqJ8pmPYSxPA0I67J
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.14 h1:zBakwHardp9Jcb8sQHcHpXy/0+JIb1M8KjigCJzx7+4=
|
||||
contrib.go.opencensus.io/exporter/stackdriver v0.13.14/go.mod h1:5pSSGY0Bhuk7waTHuDf4aQ8D2DrhgETRo9fy6k3Xlzc=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1 h1:fXPMAmuh0gDuRDey0atC8cXBuKIlqCzCkL8sm1n9Ov0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.1/go.mod h1:SUZc9YRRHfx2+FAQKNDGrssXehqLpxmwRv2mC/5ntj4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
|
||||
@ -922,10 +922,10 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s=
|
||||
google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8EeVk1VKMD+GD/neyEHGmz7pFblqPjHoi+PGQIlLx2s=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
@ -952,8 +952,8 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
@ -968,8 +968,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 h1:DkD0plWEVUB8v/Ru6kRBW30Hy/fRNBC8hPdcExuBZMc=
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0/go.mod h1:wRKMf/tRASHwH/UOfPQ3IQmVFhTz2/1a1/mpXoIjF54=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
Reference in New Issue
Block a user