Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
10476ea7d8
commit
bc9b43904d
|
|
@ -0,0 +1,25 @@
|
|||
import Vue from 'vue';
|
||||
import RefSelector from '~/ref/components/ref_selector.vue';
|
||||
|
||||
export default function initNewBranchRefSelector() {
|
||||
const el = document.querySelector('.js-new-branch-ref-selector');
|
||||
|
||||
if (!el) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { projectId, defaultBranchName, hiddenInputName } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render(createComponent) {
|
||||
return createComponent(RefSelector, {
|
||||
props: {
|
||||
value: defaultBranchName,
|
||||
name: hiddenInputName,
|
||||
projectId,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1,15 +1,9 @@
|
|||
/* eslint-disable func-names, no-return-assign, @gitlab/require-i18n-strings */
|
||||
|
||||
import $ from 'jquery';
|
||||
import RefSelectDropdown from './ref_select_dropdown';
|
||||
|
||||
export default class NewBranchForm {
|
||||
constructor(form, availableRefs) {
|
||||
constructor(form) {
|
||||
this.validate = this.validate.bind(this);
|
||||
this.branchNameError = form.querySelector('.js-branch-name-error');
|
||||
this.name = form.querySelector('.js-branch-name');
|
||||
this.ref = form.querySelector('#ref');
|
||||
new RefSelectDropdown($('.js-branch-select'), availableRefs); // eslint-disable-line no-new
|
||||
this.setupRestrictions();
|
||||
this.addBinding();
|
||||
this.init();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import NewBranchForm from '~/new_branch_form';
|
||||
import initNewBranchRefSelector from '~/branches/init_new_branch_ref_selector';
|
||||
|
||||
initNewBranchRefSelector();
|
||||
// eslint-disable-next-line no-new
|
||||
new NewBranchForm(
|
||||
document.querySelector('.js-create-branch-form'),
|
||||
JSON.parse(document.getElementById('availableRefs').innerHTML),
|
||||
);
|
||||
new NewBranchForm(document.querySelector('.js-create-branch-form'));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlButton, GlLink, GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
|
||||
import { __, s__ } from '~/locale';
|
||||
import { createAlert } from '~/flash';
|
||||
|
|
@ -10,18 +10,24 @@ import {
|
|||
STATE_OPEN,
|
||||
TASK_TYPE_NAME,
|
||||
WORK_ITEM_TYPE_VALUE_OBJECTIVE,
|
||||
WIDGET_TYPE_MILESTONE,
|
||||
WIDGET_TYPE_HIERARCHY,
|
||||
WIDGET_TYPE_ASSIGNEES,
|
||||
WIDGET_TYPE_LABELS,
|
||||
WORK_ITEM_NAME_TO_ICON_MAP,
|
||||
} from '../../constants';
|
||||
import getWorkItemTreeQuery from '../../graphql/work_item_tree.query.graphql';
|
||||
import WorkItemLinkChildMetadata from './work_item_link_child_metadata.vue';
|
||||
import WorkItemLinksMenu from './work_item_links_menu.vue';
|
||||
import WorkItemTreeChildren from './work_item_tree_children.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlLink,
|
||||
GlButton,
|
||||
GlIcon,
|
||||
RichTimestampTooltip,
|
||||
WorkItemLinkChildMetadata,
|
||||
WorkItemLinksMenu,
|
||||
WorkItemTreeChildren,
|
||||
},
|
||||
|
|
@ -67,6 +73,9 @@ export default {
|
|||
canHaveChildren() {
|
||||
return this.workItemType === WORK_ITEM_TYPE_VALUE_OBJECTIVE;
|
||||
},
|
||||
allowsScopedLabels() {
|
||||
return this.getWidgetByType(this.childItem, WIDGET_TYPE_LABELS)?.allowsScopedLabels;
|
||||
},
|
||||
isItemOpen() {
|
||||
return this.childItem.state === STATE_OPEN;
|
||||
},
|
||||
|
|
@ -95,7 +104,7 @@ export default {
|
|||
return `/${this.projectPath}/-/work_items/${getIdFromGraphQLId(this.childItem.id)}`;
|
||||
},
|
||||
hasChildren() {
|
||||
return this.getWidgetHierarchyForChild(this.childItem)?.hasChildren;
|
||||
return this.getWidgetByType(this.childItem, WIDGET_TYPE_HIERARCHY)?.hasChildren;
|
||||
},
|
||||
chevronType() {
|
||||
return this.isExpanded ? 'chevron-down' : 'chevron-right';
|
||||
|
|
@ -103,6 +112,18 @@ export default {
|
|||
chevronTooltip() {
|
||||
return this.isExpanded ? __('Collapse') : __('Expand');
|
||||
},
|
||||
hasMetadata() {
|
||||
return this.milestone || this.assignees.length > 0 || this.labels.length > 0;
|
||||
},
|
||||
milestone() {
|
||||
return this.getWidgetByType(this.childItem, WIDGET_TYPE_MILESTONE)?.milestone;
|
||||
},
|
||||
assignees() {
|
||||
return this.getWidgetByType(this.childItem, WIDGET_TYPE_ASSIGNEES)?.assignees?.nodes || [];
|
||||
},
|
||||
labels() {
|
||||
return this.getWidgetByType(this.childItem, WIDGET_TYPE_LABELS)?.labels?.nodes || [];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleItem() {
|
||||
|
|
@ -111,11 +132,8 @@ export default {
|
|||
this.fetchChildren();
|
||||
}
|
||||
},
|
||||
getWidgetHierarchyForChild(workItem) {
|
||||
const widgetHierarchy = workItem?.widgets?.find(
|
||||
(widget) => widget.type === WIDGET_TYPE_HIERARCHY,
|
||||
);
|
||||
return widgetHierarchy || {};
|
||||
getWidgetByType(workItem, widgetType) {
|
||||
return workItem?.widgets?.find((widget) => widget.type === widgetType);
|
||||
},
|
||||
async fetchChildren() {
|
||||
this.isLoadingChildren = true;
|
||||
|
|
@ -126,7 +144,7 @@ export default {
|
|||
id: this.childItem.id,
|
||||
},
|
||||
});
|
||||
this.children = this.getWidgetHierarchyForChild(data?.workItem).children.nodes;
|
||||
this.children = this.getWidgetByType(data?.workItem, WIDGET_TYPE_HIERARCHY).children.nodes;
|
||||
} catch (error) {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
createAlert({
|
||||
|
|
@ -145,7 +163,7 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="gl-display-flex gl-align-items-center gl-mb-3"
|
||||
class="gl-display-flex gl-align-items-flex-start gl-mb-3"
|
||||
:class="{ 'gl-ml-6': canHaveChildren && !hasChildren && hasIndirectChildren }"
|
||||
>
|
||||
<gl-button
|
||||
|
|
@ -156,7 +174,7 @@ export default {
|
|||
:icon="chevronType"
|
||||
category="tertiary"
|
||||
:loading="isLoadingChildren"
|
||||
class="gl-px-0! gl-py-4! gl-mr-3"
|
||||
class="gl-px-0! gl-py-3! gl-mr-3"
|
||||
data-testid="expand-child"
|
||||
@click="toggleItem"
|
||||
/>
|
||||
|
|
@ -164,40 +182,60 @@ export default {
|
|||
class="gl-relative gl-display-flex gl-flex-grow-1 gl-overflow-break-word gl-min-w-0 gl-bg-white gl-py-3 gl-px-4 gl-border gl-border-gray-100 gl-rounded-base gl-line-height-32"
|
||||
data-testid="links-child"
|
||||
>
|
||||
<div class="gl-overflow-hidden gl-display-flex gl-align-items-center gl-flex-grow-1">
|
||||
<span :id="`stateIcon-${childItem.id}`" class="gl-mr-3" data-testid="item-status-icon">
|
||||
<gl-icon
|
||||
class="gl-text-secondary"
|
||||
:class="iconClass"
|
||||
:name="iconName"
|
||||
:aria-label="stateTimestampTypeText"
|
||||
/>
|
||||
</span>
|
||||
<rich-timestamp-tooltip
|
||||
:target="`stateIcon-${childItem.id}`"
|
||||
:raw-timestamp="stateTimestamp"
|
||||
:timestamp-type-text="stateTimestampTypeText"
|
||||
/>
|
||||
<span
|
||||
:id="`stateIcon-${childItem.id}`"
|
||||
class="gl-mr-3"
|
||||
:class="{ 'gl-display-flex': hasMetadata }"
|
||||
data-testid="item-status-icon"
|
||||
>
|
||||
<gl-icon
|
||||
v-if="childItem.confidential"
|
||||
v-gl-tooltip.top
|
||||
name="eye-slash"
|
||||
class="gl-mr-2 gl-text-orange-500"
|
||||
data-testid="confidential-icon"
|
||||
:aria-label="__('Confidential')"
|
||||
:title="__('Confidential')"
|
||||
class="gl-text-secondary"
|
||||
:class="iconClass"
|
||||
:name="iconName"
|
||||
:aria-label="stateTimestampTypeText"
|
||||
/>
|
||||
</span>
|
||||
<div
|
||||
class="gl-display-flex gl-flex-grow-1"
|
||||
:class="{
|
||||
'gl-flex-direction-column gl-align-items-flex-start': hasMetadata,
|
||||
'gl-align-items-center': !hasMetadata,
|
||||
}"
|
||||
>
|
||||
<div class="gl-display-flex">
|
||||
<rich-timestamp-tooltip
|
||||
:target="`stateIcon-${childItem.id}`"
|
||||
:raw-timestamp="stateTimestamp"
|
||||
:timestamp-type-text="stateTimestampTypeText"
|
||||
/>
|
||||
<gl-icon
|
||||
v-if="childItem.confidential"
|
||||
v-gl-tooltip.top
|
||||
name="eye-slash"
|
||||
class="gl-mr-2 gl-text-orange-500"
|
||||
data-testid="confidential-icon"
|
||||
:aria-label="__('Confidential')"
|
||||
:title="__('Confidential')"
|
||||
/>
|
||||
<gl-link
|
||||
:href="childPath"
|
||||
class="gl-overflow-wrap-break gl-line-height-normal gl-text-black-normal! gl-font-weight-bold"
|
||||
data-testid="item-title"
|
||||
@click="$emit('click', $event)"
|
||||
@mouseover="$emit('mouseover')"
|
||||
@mouseout="$emit('mouseout')"
|
||||
>
|
||||
{{ childItem.title }}
|
||||
</gl-link>
|
||||
</div>
|
||||
<work-item-link-child-metadata
|
||||
v-if="hasMetadata"
|
||||
:allows-scoped-labels="allowsScopedLabels"
|
||||
:milestone="milestone"
|
||||
:assignees="assignees"
|
||||
:labels="labels"
|
||||
class="gl-mt-3"
|
||||
/>
|
||||
<gl-button
|
||||
:href="childPath"
|
||||
category="tertiary"
|
||||
variant="link"
|
||||
class="gl-text-truncate gl-max-w-80 gl-text-black-normal!"
|
||||
@click="$emit('click', $event)"
|
||||
@mouseover="$emit('mouseover')"
|
||||
@mouseout="$emit('mouseout')"
|
||||
>
|
||||
{{ childItem.title }}
|
||||
</gl-button>
|
||||
</div>
|
||||
<div
|
||||
v-if="canUpdate"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
<script>
|
||||
import { GlLabel, GlAvatar, GlAvatarLink, GlAvatarsInline, GlTooltipDirective } from '@gitlab/ui';
|
||||
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { isScopedLabel } from '~/lib/utils/common_utils';
|
||||
|
||||
import ItemMilestone from '~/issuable/components/issue_milestone.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlLabel,
|
||||
GlAvatar,
|
||||
GlAvatarLink,
|
||||
GlAvatarsInline,
|
||||
ItemMilestone,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
allowsScopedLabels: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
milestone: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
assignees: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
labels: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
assigneesCollapsedTooltip() {
|
||||
if (this.assignees.length > 2) {
|
||||
return sprintf(s__('WorkItem|%{count} more assignees'), {
|
||||
count: this.assignees.length - 2,
|
||||
});
|
||||
}
|
||||
return '';
|
||||
},
|
||||
assigneesContainerClass() {
|
||||
if (this.assignees.length === 2) {
|
||||
return 'fixed-width-avatars-2';
|
||||
} else if (this.assignees.length > 2) {
|
||||
return 'fixed-width-avatars-3';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
labelsContainerClass() {
|
||||
if (this.milestone || this.assignees.length) {
|
||||
return 'gl-sm-ml-5';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showScopedLabel(label) {
|
||||
return isScopedLabel(label) && this.allowsScopedLabels;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-display-flex gl-flex-wrap gl-align-items-center">
|
||||
<item-milestone
|
||||
v-if="milestone"
|
||||
:milestone="milestone"
|
||||
class="gl-display-flex gl-align-items-center gl-mr-5 gl-max-w-15 gl-text-secondary! gl-cursor-help! gl-text-decoration-none!"
|
||||
/>
|
||||
<gl-avatars-inline
|
||||
v-if="assignees.length"
|
||||
:avatars="assignees"
|
||||
:collapsed="true"
|
||||
:max-visible="2"
|
||||
:avatar-size="24"
|
||||
badge-tooltip-prop="name"
|
||||
:badge-sr-only-text="assigneesCollapsedTooltip"
|
||||
:class="assigneesContainerClass"
|
||||
>
|
||||
<template #avatar="{ avatar }">
|
||||
<gl-avatar-link v-gl-tooltip target="blank" :href="avatar.webUrl" :title="avatar.name">
|
||||
<gl-avatar :src="avatar.avatarUrl" :size="24" />
|
||||
</gl-avatar-link>
|
||||
</template>
|
||||
</gl-avatars-inline>
|
||||
<div v-if="labels.length" class="gl-display-flex gl-flex-wrap" :class="labelsContainerClass">
|
||||
<gl-label
|
||||
v-for="label in labels"
|
||||
:key="label.id"
|
||||
:title="label.title"
|
||||
:background-color="label.color"
|
||||
:description="label.description"
|
||||
:scoped="showScopedLabel(label)"
|
||||
class="gl-mt-2 gl-sm-mt-0 gl-mr-2 gl-mb-auto gl-label-sm"
|
||||
tooltip-placement="top"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/**
|
||||
* These overrides are needed to address https://gitlab.com/gitlab-org/gitlab-ui/-/issues/865
|
||||
*/
|
||||
.fixed-width-avatars-2 {
|
||||
width: 42px !important;
|
||||
}
|
||||
|
||||
.fixed-width-avatars-3 {
|
||||
width: 67px !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -15,6 +15,7 @@ import {
|
|||
WORK_ITEMS_TREE_TEXT_MAP,
|
||||
WORK_ITEM_TYPE_ENUM_OBJECTIVE,
|
||||
WORK_ITEM_TYPE_ENUM_KEY_RESULT,
|
||||
WORK_ITEM_TYPE_VALUE_OBJECTIVE,
|
||||
} from '../../constants';
|
||||
import workItemQuery from '../../graphql/work_item.query.graphql';
|
||||
import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql';
|
||||
|
|
@ -148,13 +149,17 @@ export default {
|
|||
});
|
||||
},
|
||||
prefetchWorkItem({ id, iid }) {
|
||||
this.prefetch = setTimeout(
|
||||
() => this.addWorkItemQuery({ id, iid }),
|
||||
DEFAULT_DEBOUNCE_AND_THROTTLE_MS,
|
||||
);
|
||||
if (this.workItemType !== WORK_ITEM_TYPE_VALUE_OBJECTIVE) {
|
||||
this.prefetch = setTimeout(
|
||||
() => this.addWorkItemQuery({ id, iid }),
|
||||
DEFAULT_DEBOUNCE_AND_THROTTLE_MS,
|
||||
);
|
||||
}
|
||||
},
|
||||
clearPrefetching() {
|
||||
clearTimeout(this.prefetch);
|
||||
if (this.prefetch) {
|
||||
clearTimeout(this.prefetch);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
iconName() {
|
||||
// TODO: Remove this once https://gitlab.com/gitlab-org/gitlab-svgs/-/merge_requests/865
|
||||
// is merged and updated in GitLab repo.
|
||||
if (this.workItemIconName === 'issue-type-keyresult') {
|
||||
return 'issue-type-key-result';
|
||||
}
|
||||
return (
|
||||
this.workItemIconName || WORK_ITEMS_TYPE_MAP[this.workItemType]?.icon || 'issue-type-issue'
|
||||
);
|
||||
|
|
|
|||
|
|
@ -127,6 +127,12 @@ export const WORK_ITEMS_TREE_TEXT_MAP = {
|
|||
title: s__('WorkItem|Child objectives and key results'),
|
||||
empty: s__('WorkItem|No objectives or key results are currently assigned.'),
|
||||
},
|
||||
[WORK_ITEM_TYPE_VALUE_ISSUE]: {
|
||||
title: s__('WorkItem|Tasks'),
|
||||
empty: s__(
|
||||
'WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts.',
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const WORK_ITEM_NAME_TO_ICON_MAP = {
|
||||
|
|
|
|||
|
|
@ -2,4 +2,7 @@ fragment MilestoneFragment on Milestone {
|
|||
expired
|
||||
id
|
||||
title
|
||||
state
|
||||
startDate
|
||||
dueDate
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
#import "~/graphql_shared/fragments/label.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/user.fragment.graphql"
|
||||
#import "~/work_items/graphql/milestone.fragment.graphql"
|
||||
|
||||
fragment WorkItemMetadataWidgets on WorkItemWidget {
|
||||
... on WorkItemWidgetMilestone {
|
||||
type
|
||||
milestone {
|
||||
...MilestoneFragment
|
||||
}
|
||||
}
|
||||
... on WorkItemWidgetAssignees {
|
||||
type
|
||||
assignees {
|
||||
nodes {
|
||||
...User
|
||||
}
|
||||
}
|
||||
}
|
||||
... on WorkItemWidgetLabels {
|
||||
type
|
||||
allowsScopedLabels
|
||||
labels {
|
||||
nodes {
|
||||
...Label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
#import "~/graphql_shared/fragments/label.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/user.fragment.graphql"
|
||||
#import "./work_item_metadata_widgets.fragment.graphql"
|
||||
|
||||
query workItemTreeQuery($id: WorkItemID!) {
|
||||
workItem(id: $id) {
|
||||
id
|
||||
|
|
@ -38,10 +42,12 @@ query workItemTreeQuery($id: WorkItemID!) {
|
|||
type
|
||||
hasChildren
|
||||
}
|
||||
...WorkItemMetadataWidgets
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
...WorkItemMetadataWidgets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#import "~/graphql_shared/fragments/label.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/user.fragment.graphql"
|
||||
#import "~/work_items/graphql/milestone.fragment.graphql"
|
||||
#import "./work_item_metadata_widgets.fragment.graphql"
|
||||
|
||||
fragment WorkItemWidgets on WorkItemWidget {
|
||||
... on WorkItemWidgetDescription {
|
||||
|
|
@ -69,6 +70,7 @@ fragment WorkItemWidgets on WorkItemWidget {
|
|||
type
|
||||
hasChildren
|
||||
}
|
||||
...WorkItemMetadataWidgets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,6 +236,13 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Remove once https: //gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/3198 is merged
|
||||
.gl-sm-ml-5 {
|
||||
@include gl-media-breakpoint-up(sm) {
|
||||
@include gl-ml-5;
|
||||
}
|
||||
}
|
||||
|
||||
/* End gitlab-ui#1709 */
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -17,18 +17,12 @@
|
|||
.form-text.text-muted.text-danger.js-branch-name-error
|
||||
.form-group.row
|
||||
= label_tag :ref, _('Create from'), class: 'col-form-label col-sm-2'
|
||||
.col-sm-10.create-from
|
||||
.dropdown
|
||||
= hidden_field_tag :ref, default_ref
|
||||
= button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select monospace', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do
|
||||
.text-left.dropdown-toggle-text= default_ref
|
||||
= sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3")
|
||||
= render 'shared/ref_dropdown', dropdown_class: 'wide'
|
||||
.col-sm-auto.create-from
|
||||
.js-new-branch-ref-selector{ data: { project_id: @project.id, default_branch_name: default_ref, hidden_input_name: 'ref' } }
|
||||
.form-text.text-muted
|
||||
= _('Existing branch name, tag, or commit SHA')
|
||||
.form-actions
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { type: 'submit', class: 'gl-mr-3' }) do
|
||||
= _('Create branch')
|
||||
= link_to _('Cancel'), project_branches_path(@project), class: 'gl-button btn btn-default btn-cancel'
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
announcement_milestone: "15.4" # (required) The milestone when this feature was first announced as deprecated.
|
||||
announcement_date: "2022-09-22" # (required) The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
|
||||
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
|
||||
removal_date: "2022-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
|
||||
removal_date: "2023-05-22" # (required) The date of the milestone release when this feature is planned to be removed. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
|
||||
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
|
||||
reporter: phikai # (required) GitLab username of the person reporting the deprecation
|
||||
stage: create # (required) String value of the stage that the feature was created in. e.g., Growth
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
announcement_milestone: "12.3"
|
||||
announcement_date: "2019-09-22"
|
||||
removal_milestone: "16.0"
|
||||
removal_date: "2023-03-22"
|
||||
removal_date: "2023-05-22"
|
||||
breaking_change: true
|
||||
reporter: tlinz
|
||||
stage: Create
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ class UpdateImportSourcesOnApplicationSettings < Gitlab::Database::Migration[2.0
|
|||
end
|
||||
|
||||
def up
|
||||
# rubocop: disable Style/GuardClause
|
||||
unless import_sources.empty?
|
||||
ApplicationSetting.update_all(import_sources: import_sources.reject { |x| x == "google_code" })
|
||||
end
|
||||
# rubocop: enable Style/GuardClause
|
||||
return if import_sources.empty?
|
||||
|
||||
new_sources = import_sources - ['google_code']
|
||||
ApplicationSetting.update_all(import_sources: new_sources.to_yaml)
|
||||
end
|
||||
|
||||
def down
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This fixes 20221209110934_update_import_sources_on_application_settings.rb, which
|
||||
# previously serialized a YAML column into a string.
|
||||
class FixUpdateImportSourcesOnApplicationSettings < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
class ApplicationSetting < MigrationRecord
|
||||
end
|
||||
|
||||
def up
|
||||
sources = ApplicationSetting.last&.import_sources
|
||||
|
||||
return unless sources.is_a?(String)
|
||||
return if sources.start_with?('---')
|
||||
|
||||
sources = YAML.safe_load(sources)
|
||||
|
||||
ApplicationSetting.update_all(import_sources: sources.to_yaml)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexIdPartitionIdToCiBuild < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
TABLE_NAME = :ci_builds
|
||||
INDEX_NAME = :index_ci_builds_on_id_partition_id_unique
|
||||
COLUMNS = %i[id partition_id].freeze
|
||||
|
||||
def up
|
||||
add_concurrent_index(TABLE_NAME, COLUMNS, unique: true, name: INDEX_NAME)
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name(TABLE_NAME, INDEX_NAME)
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
6a25429104daf2b735f0a22e48dc631ded1aebe7d6f5f9d61520af184f6b5075
|
||||
|
|
@ -0,0 +1 @@
|
|||
a961cf4e53556fe7899fbabc7bc686d5edaf061abe5a008eb7a6304f64f2f22f
|
||||
|
|
@ -28657,6 +28657,8 @@ CREATE INDEX index_ci_builds_on_commit_id_and_type_and_ref ON ci_builds USING bt
|
|||
|
||||
CREATE INDEX index_ci_builds_on_commit_id_artifacts_expired_at_and_id ON ci_builds USING btree (commit_id, artifacts_expire_at, id) WHERE (((type)::text = 'Ci::Build'::text) AND ((retried = false) OR (retried IS NULL)) AND ((name)::text = ANY (ARRAY[('sast'::character varying)::text, ('secret_detection'::character varying)::text, ('dependency_scanning'::character varying)::text, ('container_scanning'::character varying)::text, ('dast'::character varying)::text])));
|
||||
|
||||
CREATE UNIQUE INDEX index_ci_builds_on_id_partition_id_unique ON ci_builds USING btree (id, partition_id);
|
||||
|
||||
CREATE INDEX index_ci_builds_on_project_id_and_id ON ci_builds USING btree (project_id, id);
|
||||
|
||||
CREATE INDEX index_ci_builds_on_project_id_and_name_and_ref ON ci_builds USING btree (project_id, name, ref) WHERE (((type)::text = 'Ci::Build'::text) AND ((status)::text = 'success'::text) AND ((retried = false) OR (retried IS NULL)));
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ To monitor queues and disable jobs:
|
|||
|
||||
### Incident management
|
||||
|
||||
[Incident management](../../operations/incident_management/index.md) functions are limited. The creation of [alerts](../../operations/incident_management/alerts.md) and [incidents](../../operations/incident_management/incidents.md#incident-creation) are paused entirely. Notifications and paging on alerts and incidents are therefore disabled.
|
||||
[Incident management](../../operations/incident_management/index.md) functions are limited. The creation of [alerts](../../operations/incident_management/alerts.md) and [incidents](../../operations/incident_management/manage_incidents.md#create-an-incident) are paused entirely. Notifications and paging on alerts and incidents are therefore disabled.
|
||||
|
||||
### Feature flags
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Monitor
|
||||
group: Respond
|
||||
stage: Data Stores
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Monitor
|
||||
group: Respond
|
||||
stage: Data Stores
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ timeline of the alert's investigation and assignment history.
|
|||
The following actions result in a system note:
|
||||
|
||||
- [Updating the status of an alert](#change-an-alerts-status)
|
||||
- [Creating an incident based on an alert](#create-an-incident-from-an-alert)
|
||||
- [Creating an incident based on an alert](manage_incidents.md#from-an-alert)
|
||||
- [Assignment of an alert to a user](#assign-an-alert)
|
||||
- [Escalation of an alert to on-call responders](paging.md#escalating-an-alert)
|
||||
|
||||
|
|
@ -158,8 +158,8 @@ Prerequisites:
|
|||
|
||||
- You must have at least the Reporter role.
|
||||
|
||||
When you close an [incident](incidents.md) that is linked to an alert,
|
||||
the linked alert's status changes to **Resolved**.
|
||||
When you [close an incident](manage_incidents.md#close-an-incident) that is linked to an alert,
|
||||
GitLab [changes the alert's status](#change-an-alerts-status) to **Resolved**.
|
||||
You are then credited with the alert's status change.
|
||||
|
||||
#### As an on-call responder **(PREMIUM)**
|
||||
|
|
@ -173,25 +173,10 @@ Changing the status has the following effects:
|
|||
- To **Resolved**: silences all on-call pages for the alert.
|
||||
- From **Resolved** to **Triggered**: restarts the alert escalating.
|
||||
|
||||
In GitLab 15.1 and earlier, updating the status of an [alert with an associated incident](alerts.md#create-an-incident-from-an-alert)
|
||||
In GitLab 15.1 and earlier, updating the status of an [alert with an associated incident](manage_incidents.md#from-an-alert)
|
||||
also updates the incident status. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
|
||||
the incident status is independent and does not update when the alert status changes.
|
||||
|
||||
### Create an incident from an alert
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1.
|
||||
|
||||
The Alert detail view enables you to create an issue with a
|
||||
description populated from an alert. To create the issue,
|
||||
select the **Create Issue** button. You can then view the issue from the
|
||||
alert by selecting the **View Issue** button.
|
||||
|
||||
You can also [create incidents for alerts automatically](incidents.md#create-incidents-automatically).
|
||||
|
||||
Closing a GitLab issue associated with an alert [changes the alert's status](#change-an-alerts-status) to
|
||||
**Resolved**. See [Alert List](#alert-list) for more details
|
||||
about alert statuses.
|
||||
|
||||
### Assign an alert
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3066) in GitLab 13.1.
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.2 KiB |
|
|
@ -78,7 +78,7 @@ The comment is shown on the incident timeline as a timeline event.
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375280) in GitLab 15.6.
|
||||
|
||||
A new timeline event is created when someone [changes the severity](incidents.md#change-severity)
|
||||
A new timeline event is created when someone [changes the severity](manage_incidents.md#change-severity)
|
||||
of an incident.
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -6,171 +6,78 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Incidents **(FREE)**
|
||||
|
||||
Incidents are critical entities in incident management workflows. They represent
|
||||
a service disruption or outage that needs to be restored urgently. GitLab provides
|
||||
tools for the triage, response, and remediation of incidents.
|
||||
An incident is a service disruption or outage that needs to be restored urgently.
|
||||
Incidents are critical in incident management workflows.
|
||||
Use GitLab to triage, respond, and remediate incidents.
|
||||
|
||||
## Incident creation
|
||||
## Incidents list
|
||||
|
||||
You can create an incident manually or automatically.
|
||||
When you [view the incidents list](manage_incidents.md#view-incidents-list), it contains the following:
|
||||
|
||||
### Create incidents manually
|
||||
|
||||
> - [Moved](https://gitlab.com/gitlab-org/monitor/monitor/-/issues/24) to GitLab Free in 13.3.
|
||||
> - [Permission changed](https://gitlab.com/gitlab-org/gitlab/-/issues/336624) from Guest to Reporter in GitLab 14.5.
|
||||
> - Automatic application of the `incident` label [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/290964) in GitLab 14.8.
|
||||
|
||||
If you have at least Reporter [permissions](../../user/permissions.md),
|
||||
you can create an incident manually from the Incidents List or the Issues List.
|
||||
|
||||
To create an incident from the Incidents List:
|
||||
|
||||
1. Navigate to **Monitor > Incidents** and select **Create Incident**.
|
||||
1. Create a new issue using the `incident` template.
|
||||
|
||||

|
||||
|
||||
To create an incident from the Issues List:
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230857) in GitLab 13.4.
|
||||
|
||||
1. Go to **Issues > List**, and select **New issue**.
|
||||
1. In the **Type** dropdown list, select **Incident**. Only fields relevant to
|
||||
incidents are displayed on the page.
|
||||
1. Create the incident as needed, and select **Create issue** to save the
|
||||
incident.
|
||||
|
||||

|
||||
|
||||
### Create incidents automatically **(ULTIMATE)**
|
||||
|
||||
With at least the Maintainer role, you can enable
|
||||
GitLab to create incident automatically whenever an alert is triggered:
|
||||
|
||||
1. Navigate to **Settings > Monitor > Incidents** and expand **Incidents**.
|
||||
1. Check the **Create an incident** checkbox.
|
||||
1. To customize the incident, select an
|
||||
[issue template](../../user/project/description_templates.md#create-an-issue-template),
|
||||
to include in the [incident summary](#summary).
|
||||
1. To send [an email notification](paging.md#email-notifications-for-alerts) to users
|
||||
with the Developer role, select
|
||||
**Send a separate email notification to Developers**. Email notifications are
|
||||
also sent to users with the **Maintainer** and **Owner** roles.
|
||||
1. Select **Save changes**.
|
||||
|
||||
### Create incidents via the PagerDuty webhook
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3.
|
||||
> - [PagerDuty V3 Webhook](https://support.pagerduty.com/docs/webhooks) support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/383029) in GitLab 15.7.
|
||||
|
||||
You can set up a webhook with PagerDuty to automatically create a GitLab incident
|
||||
for each PagerDuty incident. This configuration requires you to make changes
|
||||
in both PagerDuty and GitLab:
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**
|
||||
1. Expand **Incidents**.
|
||||
1. Select the **PagerDuty integration** tab:
|
||||
|
||||

|
||||
|
||||
1. Activate the integration, and save the changes in GitLab.
|
||||
1. Copy the value of **Webhook URL** for use in a later step.
|
||||
1. Follow the steps described in the
|
||||
[PagerDuty documentation](https://support.pagerduty.com/docs/webhooks)
|
||||
to add the webhook URL to a PagerDuty webhook integration.
|
||||
|
||||
To confirm the integration is successful, trigger a test incident from PagerDuty to
|
||||
confirm that a GitLab incident is created from the incident.
|
||||
|
||||
## Incident list
|
||||
|
||||
Whether you can view an incident depends on the [project visibility level](../../user/public_access.md) and
|
||||
the incident's confidentiality status:
|
||||
|
||||
- Public project and a non-confidential incident: You don't have to be a member of the project.
|
||||
- Private project and non-confidential incident: You must have at least the Guest role for the project.
|
||||
- Confidential incident (regardless of project visibility): You must have at least the Reporter.
|
||||
|
||||
The Incident list is available at **Monitor > Incidents**
|
||||
in your project's sidebar. The list contains the following metrics:
|
||||
|
||||

|
||||
|
||||
- **State** - To filter incidents by their state, select **Open**, **Closed**,
|
||||
- **State**: To filter incidents by their state, select **Open**, **Closed**,
|
||||
or **All** above the incident list.
|
||||
- **Search** - The Incident list supports a simple free text search, which filters
|
||||
on the **Title** and **Incident** fields.
|
||||
- **Severity** - Severity of a particular incident, which can be one of the following
|
||||
- **Search**: Search for incident titles and descriptions or [filter the list](#filter-the-incidents-list).
|
||||
- **Severity**: Severity of a particular incident, which can be one of the following
|
||||
values:
|
||||
- **{severity-critical}** **Critical - S1**
|
||||
- **{severity-high}** **High - S2**
|
||||
- **{severity-medium}** **Medium - S3**
|
||||
- **{severity-low}** **Low - S4**
|
||||
- **{severity-unknown}** **Unknown**
|
||||
|
||||
[Editing incident severity](#change-severity) on the incident details page was
|
||||
[introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229402) in GitLab 13.4.
|
||||
- **{severity-critical}** Critical - S1
|
||||
- **{severity-high}** High - S2
|
||||
- **{severity-medium}** Medium - S3
|
||||
- **{severity-low}** Low - S4
|
||||
- **{severity-unknown}** Unknown
|
||||
|
||||
- **Incident** - The description of the incident, which attempts to capture the
|
||||
most meaningful data.
|
||||
- **Status** - The status of the incident, which can be one of the following values:
|
||||
- **Triggered**
|
||||
- **Acknowledged**
|
||||
- **Resolved**
|
||||
- **Incident**: The title of the incident, which attempts to capture the
|
||||
most meaningful information.
|
||||
- **Status**: The status of the incident, which can be one of the following values:
|
||||
|
||||
In GitLab Premium, this field is also linked to [on-call escalation](paging.md#escalating-an-incident) for the incident.
|
||||
- Triggered
|
||||
- Acknowledged
|
||||
- Resolved
|
||||
|
||||
- **Date created** - How long ago the incident was created. This field uses the
|
||||
standard GitLab pattern of `X time ago`, but is supported by a granular date/time
|
||||
tooltip depending on the user's locale.
|
||||
- **Assignees** - The user assigned to the incident.
|
||||
- **Published** - Displays a green check mark (**{check-circle}**) if the incident is published
|
||||
to a [Status Page](status_page.md).
|
||||
In the Premium or Ultimate tier, this field is also linked to [on-call escalation](paging.md#escalating-an-incident) for the incident.
|
||||
|
||||
The Incident list displays incidents sorted by incident created date.
|
||||
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) in GitLab 13.3.)
|
||||
To see if a column is sortable, point your mouse at the header. Sortable columns
|
||||
display an arrow next to the column name.
|
||||
- **Date created**: How long ago the incident was created. This field uses the
|
||||
standard GitLab pattern of `X time ago`. Hover over this value to see the exact date and time formatted according to your locale.
|
||||
- **Assignees**: The user assigned to the incident.
|
||||
- **Published**: Whether the incident is published to a [status page](status_page.md).
|
||||
|
||||
Incidents share the [Issues API](../../api/issues.md).
|
||||

|
||||
|
||||
NOTE:
|
||||
For a live example of the incident list in action, visit this
|
||||
[demo project](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
|
||||
For an example of the incident list in action, visit this
|
||||
[demo project](https://gitlab.com/gitlab-org/monitor/monitor-sandbox/-/incidents).
|
||||
|
||||
### Sort the incident list
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) in GitLab 13.3: incidents are sorted by created date by default.
|
||||
|
||||
The incident list shows incidents sorted by incident created date, showing the newest first.
|
||||
|
||||
To sort by another column, or to change the sorting order, select the column.
|
||||
|
||||
The columns you can sort by:
|
||||
|
||||
- Severity
|
||||
- Status
|
||||
- Time to SLA
|
||||
- Published
|
||||
|
||||
### Filter the incidents list
|
||||
|
||||
To filter the incident list by author or assignee, enter these values in the search box.
|
||||
|
||||
## Incident details
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230847) in GitLab 13.4.
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Monitor > Incidents**.
|
||||
1. Select an incident from the list.
|
||||
When you take any of these actions on an incident, GitLab logs a system note and
|
||||
displays it in the Incident Details view:
|
||||
|
||||
- Updating the severity of an incident
|
||||
([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42358) in GitLab 13.5.)
|
||||
|
||||
For live examples of GitLab incidents, visit the `tanuki-inc` project's
|
||||
[incident list page](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
|
||||
Select any incident in the list to display its incident details page.
|
||||
|
||||
### Summary
|
||||
|
||||
The summary section for incidents provides both critical details about the
|
||||
incident and the contents of the issue template (if applicable). The highlighted
|
||||
The summary section for incidents provides critical details about the
|
||||
incident and the contents of the issue template (if [selected](../metrics/alerts.md#trigger-actions-from-alerts)). The highlighted
|
||||
bar at the top of the incident displays from left to right:
|
||||
|
||||
- The link to the original alert.
|
||||
- The alert start time.
|
||||
- The event count.
|
||||
|
||||
Beneath the highlight bar, GitLab displays a summary that includes the following fields:
|
||||
Below the highlight bar, a summary includes the following fields:
|
||||
|
||||
- Start time
|
||||
- Severity
|
||||
|
|
@ -178,15 +85,18 @@ Beneath the highlight bar, GitLab displays a summary that includes the following
|
|||
- Monitoring tool
|
||||
|
||||
The incident summary can be further customized using
|
||||
[GitLab Flavored Markdown](../../user/markdown.md). If the corresponding alert
|
||||
[provided Markdown for the incident](../metrics/alerts.md#trigger-actions-from-alerts),
|
||||
then the Markdown is appended to the summary after the above alert fields. If an
|
||||
[incident template](#create-incidents-automatically) is configured for the
|
||||
project, then the template content is appended at the end.
|
||||
[GitLab Flavored Markdown](../../user/markdown.md).
|
||||
|
||||
If an incident is [created from an alert](../metrics/alerts.md#trigger-actions-from-alerts)
|
||||
that provided Markdown for the incident, then the Markdown is appended to the summary.
|
||||
If an incident template is configured for the project, then the template content is appended at the end.
|
||||
|
||||
Comments are displayed in threads, but can be displayed chronologically
|
||||
[by toggling on the recent updates view](#recent-updates-view).
|
||||
|
||||
When you make changes to an incident, GitLab creates system notes and
|
||||
displays them below the summary.
|
||||
|
||||
### Metrics **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235994) in GitLab 13.8.
|
||||
|
|
@ -204,6 +114,8 @@ If you add a link, you can access the original graph by selecting the hyperlink
|
|||
|
||||
### Alert details
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230847) in GitLab 13.4.
|
||||
|
||||
Incidents show the details of linked alerts in a separate tab. To populate this
|
||||
tab, the incident must have been created with a linked alert. Incidents
|
||||
created automatically from alerts have this
|
||||
|
|
@ -222,141 +134,60 @@ Read more about [timeline events](incident_timeline_events.md) and how to enable
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227836) in GitLab 13.5.
|
||||
|
||||
To quickly see the latest updates on an incident, select
|
||||
**{history}** **Turn recent updates view on** in the comment bar to display comments
|
||||
un-threaded and ordered chronologically, newest to oldest:
|
||||
|
||||

|
||||
To see the latest updates on an incident, select
|
||||
**Turn recent updates view on** (**{history}**) on the comment bar. Comments display
|
||||
un-threaded and chronologically, newest to oldest.
|
||||
|
||||
### Service Level Agreement countdown timer **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241663) in GitLab 13.5.
|
||||
|
||||
You can enable the Service Level Agreement Countdown timer on incidents to track
|
||||
the Service Level Agreements (SLAs) you hold with your customers. The timer is
|
||||
the Service Level Agreements (SLA) you hold with your customers. The timer is
|
||||
automatically started when the incident is created, and shows the time
|
||||
remaining before the SLA period expires. The timer is also dynamically updated
|
||||
every 15 minutes so you do not have to refresh the page to see the time remaining.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
To configure the timer:
|
||||
|
||||
1. Navigate to **Settings > Monitor**.
|
||||
1. Scroll to **Incidents** and select **Expand**, then select the
|
||||
**Incident settings** tab.
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**.
|
||||
1. Expand the **Incidents** section, then select the **Incident settings** tab.
|
||||
1. Select **Activate "time to SLA" countdown timer**.
|
||||
1. Set a time limit in increments of 15 minutes.
|
||||
1. Select **Save changes**.
|
||||
|
||||
After you enable the SLA countdown timer, the **Time to SLA** attribute is displayed
|
||||
as a column in the Incidents List, and as a field on newly created Incidents. If
|
||||
After you enable the SLA countdown timer, the **Time to SLA** column is available in the
|
||||
incidents list and as a field on new incidents. If
|
||||
the incident isn't closed before the SLA period ends, GitLab adds a `missed::SLA`
|
||||
label to the incident.
|
||||
|
||||
## Assign incidents
|
||||
|
||||
Assign incidents to users that are actively responding. Select **Edit** in the
|
||||
right-hand side bar to select or clear assignees.
|
||||
|
||||
## Associate a milestone
|
||||
|
||||
Associate an incident to a milestone by selecting **Edit** next to the milestone feature in the right-hand side bar.
|
||||
|
||||
## Change severity
|
||||
|
||||
See [Incident List](#incident-list) for a full description of the severity levels available.
|
||||
Select **Edit** in the right-hand side bar to change the severity of an incident.
|
||||
|
||||
You can also change the severity using the [`/severity` quick action](../../user/project/quick_actions.md).
|
||||
|
||||
## Add a to-do item
|
||||
|
||||
Add a to-do for incidents that you want to track in your to-do list. Select
|
||||
**Add a to do** at the top of the right-hand side bar to add a to-do item.
|
||||
|
||||
## Change incident status
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) in GitLab 14.10.
|
||||
> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
|
||||
|
||||
For users with the Developer role or higher, select **Edit** in the **Status** section of the
|
||||
right-hand side bar of an incident, then select a status. **Triggered** is the default status for
|
||||
new incidents.
|
||||
|
||||
In projects with GitLab Premium, on-call responders can respond to [incident pages](paging.md#escalating-an-incident)
|
||||
by changing the status. Setting the status to:
|
||||
|
||||
- **Resolved** silences on-call pages for the alert.
|
||||
- **Acknowledged** limits on-call pages based on the selected [escalation policy](#change-escalation-policy).
|
||||
- **Triggered** from **Resolved** restarts the incident escalating from the beginning.
|
||||
|
||||
In GitLab 15.1 and earlier, updating the status of an [incident created from an alert](alerts.md#create-an-incident-from-an-alert)
|
||||
also updates the alert status. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
|
||||
the alert status is independent and does not update when the incident status changes.
|
||||
|
||||
## Change escalation policy **(PREMIUM)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) in GitLab 14.10.
|
||||
> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
|
||||
|
||||
For users with the Developer role or higher, select **Edit** in the **Escalation policy** section of
|
||||
the right-hand side bar of an incident, then select a policy. By default, new incidents do not have
|
||||
an escalation policy selected.
|
||||
|
||||
Selecting an escalation policy updates the incident status to **Triggered** and begins
|
||||
[escalating the incident to on-call responders](paging.md#escalating-an-incident).
|
||||
Deselecting an escalation policy halts escalation. Refer to the [incident status](#change-incident-status)
|
||||
to manage on-call paging once escalation has begun.
|
||||
|
||||
In GitLab 15.1 and earlier, the escalation policy for [incidents created from alerts](alerts.md#create-an-incident-from-an-alert)
|
||||
reflects the alert's escalation policy and cannot be changed. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
|
||||
the incident escalation policy is independent and can be changed.
|
||||
|
||||
## Associate Zoom calls
|
||||
|
||||
GitLab enables you to [associate a Zoom meeting with an issue](../../user/project/issues/associate_zoom_meeting.md)
|
||||
for synchronous communication during incident management. After starting a Zoom
|
||||
call for an incident, you can associate the conference call with an issue. Your
|
||||
team members can join the Zoom call without requesting a link.
|
||||
|
||||
## Linked resources
|
||||
|
||||
In an incident, you can add [links to various resources](linked_resources.md),
|
||||
for example:
|
||||
|
||||
- The incident Slack channel
|
||||
- Zoom meeting
|
||||
- Resources for resolving the incidents
|
||||
|
||||
## Embed metrics in incidents
|
||||
|
||||
You can embed metrics anywhere [GitLab Markdown](../../user/markdown.md) is
|
||||
used, such as descriptions, comments on issues, and merge requests. Embedding
|
||||
metrics helps you share them when discussing incidents or performance issues.
|
||||
You can output the dashboard directly into any issue, merge request, epic, or
|
||||
any other Markdown text field in GitLab by
|
||||
[copying and pasting the link to the metrics dashboard](../metrics/embed.md#embedding-gitlab-managed-kubernetes-metrics).
|
||||
|
||||
You can embed both [GitLab-hosted metrics](../metrics/embed.md) and
|
||||
[Grafana metrics](../metrics/embed_grafana.md) in incidents and issue
|
||||
templates.
|
||||
|
||||
## Automatically close incidents via recovery alerts
|
||||
|
||||
> - [Introduced for Prometheus Integrations](https://gitlab.com/gitlab-org/gitlab/-/issues/13401) in GitLab 12.5.
|
||||
> - [Introduced for HTTP Integrations](https://gitlab.com/gitlab-org/gitlab/-/issues/13402) in GitLab 13.4.
|
||||
|
||||
With at least the Maintainer role, you can enable
|
||||
GitLab to close an incident automatically when a **Recovery Alert** is received:
|
||||
|
||||
1. Navigate to **Settings > Monitor > Incidents** and expand **Incidents**.
|
||||
1. Check the **Automatically close associated Incident** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
When GitLab receives a **Recovery Alert**, it closes the associated incident.
|
||||
This action is recorded as a system message on the incident indicating that it
|
||||
was closed automatically by the GitLab Alert bot.
|
||||
|
||||
## Related topics
|
||||
|
||||
- [Create an incident](manage_incidents.md#create-an-incident)
|
||||
- [Create an incident automatically](../metrics/alerts.md#trigger-actions-from-alerts)
|
||||
whenever an alert is triggered
|
||||
- [View incidents list](manage_incidents.md#view-incidents-list)
|
||||
- [Assign to a user](manage_incidents.md#assign-to-a-user)
|
||||
- [Change incident severity](manage_incidents.md#change-severity)
|
||||
- [Change incident status](manage_incidents.md#change-status)
|
||||
- [Change escalation policy](manage_incidents.md#change-escalation-policy)
|
||||
- [Embed metrics](manage_incidents.md#embed-metrics)
|
||||
- [Close an incident](manage_incidents.md#close-an-incident)
|
||||
- [Automatically close incidents via recovery alerts](manage_incidents.md#automatically-close-incidents-via-recovery-alerts)
|
||||
- [Add a to-do item](../../user/todos.md#create-a-to-do-item)
|
||||
- [Add labels](../../user/project/labels.md)
|
||||
- [Assign a milestone](../../user/project/milestones/index.md)
|
||||
- [Make an incident confidential](../../user/project/issues/confidential_issues.md)
|
||||
- [Set a due date](../../user/project/issues/due_dates.md)
|
||||
- [Toggle notifications](../../user/profile/notifications.md#edit-notification-settings-for-issues-merge-requests-and-epics)
|
||||
- [Track spent time](../../user/project/time_tracking.md)
|
||||
- [Add a Zoom meeting to an incident](../../user/project/issues/associate_zoom_meeting.md) the same
|
||||
way you add it to an issue.
|
||||
- [Linked resources in incidents](linked_resources.md)
|
||||
- Create incidents and receive incident notifications [directly from Slack](slack.md).
|
||||
- Use the [Issues API](../../api/issues.md) to interact with incidents.
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ receives a payload with the end time of the alert set. For HTTP Endpoints
|
|||
without [custom mappings](#map-fields-in-custom-alerts), the expected
|
||||
field is `end_time`. With custom mappings, you can select the expected field.
|
||||
|
||||
You can also configure the associated [incident to be closed automatically](../incident_management/incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
|
||||
You can also configure the associated [incident to be closed automatically](../incident_management/manage_incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
|
||||
|
||||
## Link to your Opsgenie Alerts **(PREMIUM)**
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,263 @@
|
|||
---
|
||||
stage: Monitor
|
||||
group: Respond
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Manage incidents **(FREE)**
|
||||
|
||||
This page collects instructions for all the things you can do with [incidents](incidents.md) or in relation to them.
|
||||
|
||||
## Create an incident
|
||||
|
||||
You can create an incident manually or automatically.
|
||||
|
||||
### From the incidents list
|
||||
|
||||
> - [Moved](https://gitlab.com/gitlab-org/monitor/monitor/-/issues/24) to GitLab Free in 13.3.
|
||||
> - [Permission changed](https://gitlab.com/gitlab-org/gitlab/-/issues/336624) from Guest to Reporter in GitLab 14.5.
|
||||
> - Automatic application of the `incident` label [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/290964) in GitLab 14.8.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role for the project.
|
||||
|
||||
To create an incident from the incidents list:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Monitor > Incidents**.
|
||||
1. Select **Create incident**.
|
||||
|
||||
### From the issues list
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230857) in GitLab 13.4.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role for the project.
|
||||
|
||||
To create an incident from the issues list:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Issues > List**, and select **New issue**.
|
||||
1. From the **Type** dropdown list, select **Incident**. Only fields relevant to
|
||||
incidents are available on the page.
|
||||
1. Select **Create issue**.
|
||||
|
||||
### From an alert
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/217745) in GitLab 13.1.
|
||||
|
||||
Create an incident issue when viewing an [alert](alerts.md).
|
||||
The incident description is populated from the alert.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role for the project.
|
||||
|
||||
To create an incident from an alert:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Monitor > Alerts**.
|
||||
1. Select your desired alert.
|
||||
1. Select **Create incident**.
|
||||
|
||||
After an incident is created, to view it from the alert, select **View incident**.
|
||||
|
||||
When you [close an incident](#close-an-incident) linked to an alert, GitLab
|
||||
[changes the alert's status](alerts.md#change-an-alerts-status) to **Resolved**.
|
||||
You are then credited with the alert's status change.
|
||||
|
||||
### Automatically, when an alert is triggered **(ULTIMATE)**
|
||||
|
||||
In the project settings, you can turn on [creating an incident automatically](../metrics/alerts.md#trigger-actions-from-alerts)
|
||||
whenever an alert is triggered.
|
||||
|
||||
### Using the PagerDuty webhook
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/119018) in GitLab 13.3.
|
||||
> - [PagerDuty V3 Webhook](https://support.pagerduty.com/docs/webhooks) support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/383029) in GitLab 15.7.
|
||||
|
||||
You can set up a webhook with PagerDuty to automatically create a GitLab incident
|
||||
for each PagerDuty incident. This configuration requires you to make changes
|
||||
in both PagerDuty and GitLab.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
To set up a webhook with PagerDuty:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**
|
||||
1. Expand **Incidents**.
|
||||
1. Select the **PagerDuty integration** tab.
|
||||
1. Turn on the **Active** toggle.
|
||||
1. Select **Save integration**.
|
||||
1. Copy the value of **Webhook URL** for use in a later step.
|
||||
1. To add the webhook URL to a PagerDuty webhook integration, follow the steps described in the [PagerDuty documentation](https://support.pagerduty.com/docs/webhooks#manage-v3-webhook-subscriptions).
|
||||
|
||||
To confirm the integration is successful, trigger a test incident from PagerDuty to
|
||||
check if a GitLab incident is created from the incident.
|
||||
|
||||
## View incidents list
|
||||
|
||||
To view the [incidents list](incidents.md#incidents-list):
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Monitor > Incidents**.
|
||||
|
||||
To view an incident's [details page](incidents.md#incident-details), select it from the list.
|
||||
|
||||
### Who can view an incident
|
||||
|
||||
Whether you can view an incident depends on the [project visibility level](../../user/public_access.md) and
|
||||
the incident's confidentiality status:
|
||||
|
||||
- Public project and a non-confidential incident: You don't have to be a member of the project.
|
||||
- Private project and non-confidential incident: You must have at least the Guest role for the project.
|
||||
- Confidential incident (regardless of project visibility): You must have at least the Reporter role for the project.
|
||||
|
||||
## Assign to a user
|
||||
|
||||
Assign incidents to users that are actively responding.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role for the project.
|
||||
|
||||
To assign a user:
|
||||
|
||||
1. In an incident, on the right sidebar, next to **Assignees**, select **Edit**.
|
||||
1. From the dropdown list, select one or [multiple users](../../user/project/issues/multiple_assignees_for_issues.md) to add as **assignees**.
|
||||
1. Select any area outside the dropdown list.
|
||||
|
||||
## Change severity
|
||||
|
||||
> Editing severity on incident details page was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229402) in GitLab 13.4.
|
||||
|
||||
See [incident list](incidents.md#incidents-list) for a full description of the severity levels available.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role for the project.
|
||||
|
||||
To change an incident's severity:
|
||||
|
||||
1. In an incident, on the right sidebar, next to **Severity**, select **Edit**.
|
||||
1. From the dropdown list, select the new severity.
|
||||
|
||||
You can also change the severity using the `/severity` [quick action](../../user/project/quick_actions.md).
|
||||
|
||||
## Change status
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) in GitLab 14.10.
|
||||
> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role for the project.
|
||||
|
||||
To change the status of an incident:
|
||||
|
||||
1. In an incident, on the right sidebar, next to **Status**, select **Edit**.
|
||||
1. From the dropdown list, select the new severity.
|
||||
|
||||
**Triggered** is the default status for new incidents.
|
||||
|
||||
### As an on-call responder **(PREMIUM)**
|
||||
|
||||
On-call responders can respond to [incident pages](paging.md#escalating-an-incident)
|
||||
by changing the status.
|
||||
|
||||
Changing the status has the following effects:
|
||||
|
||||
- To **Acknowledged**: limits on-call pages based on the project's [escalation policy](escalation_policies.md).
|
||||
- To **Resolved**: silences all on-call pages for the incident.
|
||||
- From **Resolved** to **Triggered**: restarts the incident escalating.
|
||||
|
||||
In GitLab 15.1 and earlier, changing the status of an [incident created from an alert](#from-an-alert)
|
||||
also changes the alert status. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
|
||||
the alert status is independent and does not change when the incident status changes.
|
||||
|
||||
## Change escalation policy **(PREMIUM)**
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Developer role for the project.
|
||||
|
||||
To change the escalation policy of an incident:
|
||||
|
||||
1. In an incident, on the right sidebar, next to **Escalation policy**, select **Edit**.
|
||||
1. From the dropdown list, select the escalation policy.
|
||||
|
||||
By default, new incidents do not have an escalation policy selected.
|
||||
|
||||
Selecting an escalation policy [changes the incident status](#change-status) to **Triggered** and begins
|
||||
[escalating the incident to on-call responders](paging.md#escalating-an-incident).
|
||||
|
||||
In GitLab 15.1 and earlier, the escalation policy for [incidents created from alerts](#from-an-alert)
|
||||
reflects the alert's escalation policy and cannot be changed. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
|
||||
the incident escalation policy is independent and can be changed.
|
||||
|
||||
## Embed metrics
|
||||
|
||||
You can embed metrics anywhere [GitLab Flavored Markdown](../../user/markdown.md) is
|
||||
used, like descriptions or comments. Embedding
|
||||
metrics helps you share them when discussing incidents or performance issues.
|
||||
|
||||
To embed metrics in a Markdown text box in GitLab,
|
||||
[paste the link to the dashboard](../metrics/embed.md#embedding-gitlab-managed-kubernetes-metrics).
|
||||
|
||||
You can embed both [GitLab-hosted metrics](../metrics/embed.md) (deprecated) and
|
||||
[Grafana metrics](../metrics/embed_grafana.md) in incidents and issue
|
||||
templates.
|
||||
|
||||
## Close an incident
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Reporter role for the project.
|
||||
|
||||
To close an incident, in the top right, select **Close incident**.
|
||||
|
||||
When you close an incident that is linked to an [alert](alerts.md),
|
||||
the linked alert's status changes to **Resolved**.
|
||||
You are then credited with the alert's status change.
|
||||
|
||||
### Automatically close incidents via recovery alerts
|
||||
|
||||
> [Introduced for HTTP integrations](https://gitlab.com/gitlab-org/gitlab/-/issues/13402) in GitLab 13.4.
|
||||
|
||||
Turn on closing an incident automatically when GitLab receives a recovery alert
|
||||
from a HTTP or Prometheus webhook.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
To configure the setting:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**.
|
||||
1. Expand the **Incidents** section.
|
||||
1. Select the **Automatically close associated incident** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
When GitLab receives a recovery alert, it closes the associated incident.
|
||||
This action is recorded as a system note on the incident indicating that it
|
||||
was closed automatically by the GitLab Alert bot.
|
||||
|
||||
## Other actions
|
||||
|
||||
Because incidents in GitLab are built on top of [issues](../../user/project/issues/index.md),
|
||||
they have the following actions in common:
|
||||
|
||||
- [Add a to-do item](../../user/todos.md#create-a-to-do-item)
|
||||
- [Add labels](../../user/project/labels.md#assign-and-unassign-labels)
|
||||
- [Assign a milestone](../../user/project/milestones/index.md#assign-a-milestone-to-an-issue-or-merge-request)
|
||||
- [Make an incident confidential](../../user/project/issues/confidential_issues.md)
|
||||
- [Set a due date](../../user/project/issues/due_dates.md)
|
||||
- [Toggle notifications](../../user/profile/notifications.md#edit-notification-settings-for-issues-merge-requests-and-epics)
|
||||
- [Track time spent](../../user/project/time_tracking.md)
|
||||
|
|
@ -54,12 +54,14 @@ or stop alert escalations by [updating the alert's status](alerts.md#change-an-a
|
|||
> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
|
||||
|
||||
For incidents, paging on-call responders is optional for each individual incident.
|
||||
To begin escalating the incident, [set the incident's escalation policy](incidents.md#change-escalation-policy).
|
||||
For each escalation rule, the designated on-call responders receive one email when
|
||||
the rule fires. You can respond to a page or stop incident escalations by
|
||||
[updating the incident's status](incidents.md#change-incident-status) or, if applicable,
|
||||
[unsetting the incident's escalation policy](incidents.md#change-escalation-policy).
|
||||
|
||||
In GitLab 15.1 and earlier, [incidents created from alerts](alerts.md#create-an-incident-from-an-alert)
|
||||
To begin escalating the incident, [set the incident's escalation policy](manage_incidents.md#change-escalation-policy).
|
||||
|
||||
For each escalation rule, the designated on-call responders receive one email when
|
||||
the rule fires. Respond to a page or stop incident escalations by
|
||||
[changing the incident's status](manage_incidents.md#change-status) or
|
||||
changing the incident's escalation policy back to **No escalation policy**.
|
||||
|
||||
In GitLab 15.1 and earlier, [incidents created from alerts](manage_incidents.md#from-an-alert)
|
||||
do not support independent escalation. In [GitLab 15.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356057),
|
||||
all incidents can be escalated independently.
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ To declare a GitLab incident from Slack:
|
|||
- The project where the incident should be created.
|
||||
- The severity of the incident.
|
||||
|
||||
If there is an existing [incident template](incidents.md#create-incidents-automatically) for your
|
||||
If there is an existing [incident template](../metrics/alerts.md#trigger-actions-from-alerts) for your
|
||||
project, that template is automatically applied to the description text box. The template is applied
|
||||
only if the description text box is empty.
|
||||
|
||||
|
|
|
|||
|
|
@ -17,27 +17,44 @@ Alerts are not supported for [Prometheus cluster integrations](../../user/cluste
|
|||
|
||||
## Trigger actions from alerts **(ULTIMATE)**
|
||||
|
||||
Alerts can be used to trigger actions, like opening an issue automatically
|
||||
(disabled by default since `13.1`). To configure the actions:
|
||||
> - Introduced in GitLab 13.1: incidents are not created automatically by default .
|
||||
> - Mapping common severity values from the alert payload ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50871) in GitLab 13.9.
|
||||
|
||||
1. Navigate to your project's **Settings > Monitor > Alerts**.
|
||||
1. Enable the option to create issues.
|
||||
1. Choose the [issue template](../../user/project/description_templates.md) to create the issue from.
|
||||
1. Optionally, select whether to send an email notification to the developers of the project.
|
||||
Turn on creating [incidents](../incident_management/incidents.md) automatically whenever an alert is triggered.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the Maintainer role for the project.
|
||||
|
||||
To configure the actions:
|
||||
|
||||
1. On the top bar, select **Main menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**.
|
||||
1. Expand the **Alerts** section, then select the **Alert settings** tab.
|
||||
1. Select the **Create an incident** checkbox.
|
||||
1. Optional. To customize the incident, from the **Incident template**, select a template to be
|
||||
appended to the [incident summary](../incident_management/incidents.md#summary).
|
||||
If the dropdown list is empty,
|
||||
[create an issue template](../../user/project/description_templates.md#create-an-issue-template) first.
|
||||
1. Optional. To send [an email notification](../incident_management/paging.md#email-notifications-for-alerts), select the
|
||||
**Send a single email notification to Owners and Maintainers for new alerts** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
After enabling, GitLab automatically opens an issue when an alert is triggered containing
|
||||
values extracted from the [`alerts` field in webhook payload](https://prometheus.io/docs/alerting/latest/configuration/#webhook_config):
|
||||
### Fields in automatically created incidents
|
||||
|
||||
- Issue author: `GitLab Alert Bot`
|
||||
- Issue title: Extracted from the alert payload fields `annotations/title`, `annotations/summary`, or `labels/alertname`.
|
||||
- Issue description: Extracted from alert payload field `annotations/description`.
|
||||
Incidents [created automatically from an alert](#trigger-actions-from-alerts) are filled with
|
||||
values extracted from the `alerts` field in the
|
||||
[webhook payload](https://prometheus.io/docs/alerting/latest/configuration/#webhook_config):
|
||||
|
||||
- Incident author: `GitLab Alert Bot`
|
||||
- Incident title: Extracted from the alert payload fields `annotations/title`, `annotations/summary`, or `labels/alertname`.
|
||||
- Incident description: Extracted from alert payload field `annotations/description`.
|
||||
- Alert `Summary`: A list of properties from the alert's payload.
|
||||
- `starts_at`: Alert start time from the payload's `startsAt` field
|
||||
- `full_query`: Alert query extracted from the payload's `generatorURL` field
|
||||
- Optional list of attached annotations extracted from `annotations/*`
|
||||
- Alert [GLFM](../../user/markdown.md): GitLab Flavored Markdown from the payload's `annotations/gitlab_incident_markdown` field.
|
||||
- Alert Severity ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50871) in GitLab version 13.9):
|
||||
- Alert severity:
|
||||
Extracted from the alert payload field `labels/severity`. Maps case-insensitive
|
||||
value to [Alert's severity](../incident_management/alerts.md#alert-severity):
|
||||
- **Critical**: `critical`, `s1`, `p1`, `emergency`, `fatal`, or any value not in this list
|
||||
|
|
@ -46,23 +63,17 @@ values extracted from the [`alerts` field in webhook payload](https://prometheus
|
|||
- **Low**: `low`, `s4`, `p4`, `warn`, `warning`
|
||||
- **Info**: `info`, `s5`, `p5`, `debug`, `information`, `notice`
|
||||
|
||||
To further customize the issue, you can add labels, mentions, or any other supported
|
||||
To further customize the incident, you can add labels, mentions, or any other supported
|
||||
[quick action](../../user/project/quick_actions.md) in the selected issue template,
|
||||
which applies to all incidents. To limit quick actions or other information to
|
||||
only specific types of alerts, use the `annotations/gitlab_incident_markdown` field.
|
||||
|
||||
Since [version 12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/63373),
|
||||
GitLab tags each incident issue with the `incident` label automatically. If the label
|
||||
does not yet exist, it is also created automatically.
|
||||
|
||||
If the metric exceeds the threshold of the alert for over 5 minutes, GitLab sends
|
||||
an email to all Maintainers and Owners of the project.
|
||||
does not yet exist, it's created automatically.
|
||||
|
||||
### Recovery alerts
|
||||
|
||||
> [From GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/-/issues/13401), when GitLab receives a recovery alert, it automatically closes the associated issue.
|
||||
|
||||
The alert in GitLab will be automatically resolved when Prometheus
|
||||
The alert in GitLab is automatically resolved when Prometheus
|
||||
sends a payload with the field `status` set to `resolved`.
|
||||
|
||||
You can also configure the associated [incident to be closed automatically](../incident_management/incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
|
||||
You can also configure the associated [incident to be closed automatically](../incident_management/manage_incidents.md#automatically-close-incidents-via-recovery-alerts) when the alert resolves.
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ GitLab's operational container scanning capabilities no longer require starboard
|
|||
|
||||
### Toggle behavior of `/draft` quick action in merge requests
|
||||
|
||||
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2022-05-22)
|
||||
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
|
||||
|
||||
WARNING:
|
||||
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
|
||||
|
|
|
|||
|
|
@ -79,14 +79,17 @@ The following table lists project permissions available for each role:
|
|||
| [GitLab Pages](project/pages/index.md):<br>Manage | | | | ✓ | ✓ |
|
||||
| [GitLab Pages](project/pages/index.md):<br>Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
|
||||
| [GitLab Pages](project/pages/index.md):<br>Remove GitLab Pages | | | | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>View [alerts](../operations/incident_management/alerts.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Assign an alert | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>[Change an alert status](../operations/incident_management/alerts.md#change-an-alerts-status) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>View [incident](../operations/incident_management/incidents.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Create [incident](../operations/incident_management/incidents.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>View [on-call schedules](../operations/incident_management/oncall_schedules.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Participate in on-call rotation | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>View [incident](../operations/incident_management/incidents.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Change [alert status](../operations/incident_management/alerts.md#change-an-alerts-status) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Change [incident severity](../operations/incident_management/manage_incidents.md#change-severity) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Create [incident](../operations/incident_management/incidents.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>View [alerts](../operations/incident_management/alerts.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>View [escalation policies](../operations/incident_management/escalation_policies.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>View [on-call schedules](../operations/incident_management/oncall_schedules.md) | | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Change [incident escalation status](../operations/incident_management/manage_incidents.md#change-status) | | | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Change [incident escalation policy](../operations/incident_management/manage_incidents.md#change-escalation-policy) | | | ✓ | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Manage [on-call schedules](../operations/incident_management/oncall_schedules.md) | | | | ✓ | ✓ |
|
||||
| [Incident Management](../operations/incident_management/index.md):<br>Manage [escalation policies](../operations/incident_management/escalation_policies.md) | | | | ✓ | ✓ |
|
||||
| [Issue boards](project/issue_board.md):<br>Create or delete lists | | ✓ | ✓ | ✓ | ✓ |
|
||||
|
|
|
|||
|
|
@ -362,11 +362,11 @@ Configure [alert integrations](../../../operations/incident_management/integrati
|
|||
|
||||
#### Alert integration
|
||||
|
||||
Automatically [create](../../../operations/incident_management/incidents.md#create-incidents-automatically), [notify on](../../../operations/incident_management/paging.md#email-notifications-for-alerts), and [resolve](../../../operations/incident_management/incidents.md#automatically-close-incidents-via-recovery-alerts) incidents based on GitLab alerts.
|
||||
Automatically [create](../../../operations/metrics/alerts.md#trigger-actions-from-alerts), [notify on](../../../operations/incident_management/paging.md#email-notifications-for-alerts), and [resolve](../../../operations/incident_management/manage_incidents.md#automatically-close-incidents-via-recovery-alerts) incidents based on GitLab alerts.
|
||||
|
||||
#### PagerDuty integration
|
||||
|
||||
[Create incidents in GitLab for each PagerDuty incident](../../../operations/incident_management/incidents.md#create-incidents-via-the-pagerduty-webhook).
|
||||
[Create incidents in GitLab for each PagerDuty incident](../../../operations/incident_management/manage_incidents.md#using-the-pagerduty-webhook).
|
||||
|
||||
#### Incident settings
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ You can manually add an item to your To-Do List.
|
|||
- [Merge request](project/merge_requests/index.md)
|
||||
- [Epic](group/epics/index.md)
|
||||
- [Design](project/issues/design_management.md)
|
||||
- [Incident](../operations/incident_management/incidents.md)
|
||||
|
||||
1. On the right sidebar, at the top, select **Add a to do**.
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ module Gitlab
|
|||
def execute
|
||||
return if merge_request_id.blank?
|
||||
|
||||
note.project = project
|
||||
note.merge_request = merge_request
|
||||
|
||||
build_author_attributes
|
||||
|
|
|
|||
|
|
@ -4,18 +4,15 @@ module Gitlab
|
|||
module GithubImport
|
||||
module Representation
|
||||
class DiffNote
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include ToHash
|
||||
include ExposeAttribute
|
||||
|
||||
NOTEABLE_TYPE = 'MergeRequest'
|
||||
NOTEABLE_ID_REGEX = %r{/pull/(?<iid>\d+)}i.freeze
|
||||
DISCUSSION_CACHE_KEY = 'github-importer/discussion-id-map/%{project_id}/%{noteable_id}/%{original_note_id}'
|
||||
|
||||
expose_attribute :noteable_id, :commit_id, :file_path,
|
||||
:diff_hunk, :author, :created_at, :updated_at,
|
||||
:original_commit_id, :note_id, :end_line, :start_line,
|
||||
:side, :in_reply_to_id
|
||||
:side, :in_reply_to_id, :discussion_id
|
||||
|
||||
# Builds a diff note from a GitHub API response.
|
||||
#
|
||||
|
|
@ -45,7 +42,8 @@ module Gitlab
|
|||
end_line: note[:line],
|
||||
start_line: note[:start_line],
|
||||
side: note[:side],
|
||||
in_reply_to_id: note[:in_reply_to_id]
|
||||
in_reply_to_id: note[:in_reply_to_id],
|
||||
discussion_id: DiffNotes::DiscussionId.new(note).find_or_generate
|
||||
}
|
||||
|
||||
new(hash)
|
||||
|
|
@ -59,7 +57,7 @@ module Gitlab
|
|||
new(hash)
|
||||
end
|
||||
|
||||
attr_accessor :merge_request, :project
|
||||
attr_accessor :merge_request
|
||||
|
||||
# attributes - A Hash containing the raw note details. The keys of this
|
||||
# Hash must be Symbols.
|
||||
|
|
@ -74,7 +72,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def noteable_type
|
||||
NOTEABLE_TYPE
|
||||
DiffNotes::DiscussionId::NOTEABLE_TYPE
|
||||
end
|
||||
|
||||
def contains_suggestion?
|
||||
|
|
@ -127,12 +125,6 @@ module Gitlab
|
|||
}
|
||||
end
|
||||
|
||||
def discussion_id
|
||||
strong_memoize(:discussion_id) do
|
||||
(in_reply_to_id.present? && current_discussion_id) || generate_discussion_id
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Required by ExposeAttribute
|
||||
|
|
@ -149,32 +141,6 @@ module Gitlab
|
|||
def addition?
|
||||
side == 'RIGHT'
|
||||
end
|
||||
|
||||
def generate_discussion_id
|
||||
Discussion.discussion_id(
|
||||
Struct
|
||||
.new(:noteable_id, :noteable_type)
|
||||
.new(merge_request.id, NOTEABLE_TYPE)
|
||||
).tap do |discussion_id|
|
||||
cache_discussion_id(discussion_id)
|
||||
end
|
||||
end
|
||||
|
||||
def cache_discussion_id(discussion_id)
|
||||
Gitlab::Cache::Import::Caching.write(discussion_id_cache_key(note_id), discussion_id)
|
||||
end
|
||||
|
||||
def current_discussion_id
|
||||
Gitlab::Cache::Import::Caching.read(discussion_id_cache_key(in_reply_to_id))
|
||||
end
|
||||
|
||||
def discussion_id_cache_key(id)
|
||||
DISCUSSION_CACHE_KEY % {
|
||||
project_id: project.id,
|
||||
noteable_id: merge_request.id,
|
||||
original_note_id: id
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Representation
|
||||
module DiffNotes
|
||||
class DiscussionId
|
||||
NOTEABLE_TYPE = 'MergeRequest'
|
||||
DISCUSSION_CACHE_REGEX = %r{/(?<repo>[^/]*)/pull/(?<iid>\d+)}i.freeze
|
||||
DISCUSSION_CACHE_KEY = 'github-importer/discussion-id-map/%{project}/%{noteable_id}/%{original_note_id}'
|
||||
|
||||
def initialize(note)
|
||||
@note = note
|
||||
@matches = note[:html_url].match(DISCUSSION_CACHE_REGEX)
|
||||
end
|
||||
|
||||
def find_or_generate
|
||||
(note[:in_reply_to_id].present? && current_discussion_id) || generate_discussion_id
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :note, :matches
|
||||
|
||||
def generate_discussion_id
|
||||
discussion_id = Discussion.discussion_id(
|
||||
Struct
|
||||
.new(:noteable_id, :noteable_type)
|
||||
.new(matches[:iid].to_i, NOTEABLE_TYPE)
|
||||
)
|
||||
cache_discussion_id(discussion_id)
|
||||
end
|
||||
|
||||
def cache_discussion_id(discussion_id)
|
||||
Gitlab::Cache::Import::Caching.write(
|
||||
discussion_id_cache_key(note[:id]), discussion_id
|
||||
)
|
||||
end
|
||||
|
||||
def current_discussion_id
|
||||
Gitlab::Cache::Import::Caching.read(
|
||||
discussion_id_cache_key(note[:in_reply_to_id])
|
||||
)
|
||||
end
|
||||
|
||||
def discussion_id_cache_key(id)
|
||||
format(DISCUSSION_CACHE_KEY,
|
||||
project: matches[:repo],
|
||||
noteable_id: matches[:iid].to_i,
|
||||
original_note_id: id
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -44867,6 +44867,12 @@ msgstr ""
|
|||
msgid "User %{username} was successfully removed."
|
||||
msgstr ""
|
||||
|
||||
msgid "User %{user} SCIM identity is deactivated"
|
||||
msgstr ""
|
||||
|
||||
msgid "User %{user} SCIM identity is reactivated"
|
||||
msgstr ""
|
||||
|
||||
msgid "User %{user} was removed from %{group}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -46905,6 +46911,9 @@ msgstr ""
|
|||
msgid "Work in progress Limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|%{count} more assignees"
|
||||
msgstr ""
|
||||
|
||||
msgid "WorkItem|%{workItemType} deleted"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ require 'spec_helper'
|
|||
RSpec.describe 'New Branch Ref Dropdown', :js, feature_category: :projects do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :public, :repository) }
|
||||
let(:toggle) { find('.create-from .dropdown-menu-toggle') }
|
||||
let(:sha) { project.commit.sha }
|
||||
let(:toggle) { find('.ref-selector') }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
|
@ -14,37 +15,75 @@ RSpec.describe 'New Branch Ref Dropdown', :js, feature_category: :projects do
|
|||
visit new_project_branch_path(project)
|
||||
end
|
||||
|
||||
it 'filters a list of branches and tags' do
|
||||
it 'finds a tag in a list' do
|
||||
tag_name = 'v1.0.0'
|
||||
|
||||
toggle.click
|
||||
|
||||
filter_by('v1.0.0')
|
||||
filter_by(tag_name)
|
||||
|
||||
expect(items_count).to be(1)
|
||||
wait_for_requests
|
||||
|
||||
filter_by('video')
|
||||
expect(items_count(tag_name)).to be(1)
|
||||
|
||||
expect(items_count).to be(1)
|
||||
item(tag_name).click
|
||||
|
||||
find('.create-from .dropdown-content li').click
|
||||
|
||||
expect(toggle).to have_content 'video'
|
||||
expect(toggle).to have_content tag_name
|
||||
end
|
||||
|
||||
it 'accepts a manually entered commit SHA' do
|
||||
it 'finds a branch in a list' do
|
||||
branch_name = 'audio'
|
||||
|
||||
toggle.click
|
||||
|
||||
filter_by('somecommitsha')
|
||||
filter_by(branch_name)
|
||||
|
||||
find('.create-from input[type=search]').send_keys(:enter)
|
||||
wait_for_requests
|
||||
|
||||
expect(toggle).to have_content 'somecommitsha'
|
||||
expect(items_count(branch_name)).to be(1)
|
||||
|
||||
item(branch_name).click
|
||||
|
||||
expect(toggle).to have_content branch_name
|
||||
end
|
||||
|
||||
def items_count
|
||||
all('.create-from .dropdown-content li').length
|
||||
it 'finds a commit in a list' do
|
||||
toggle.click
|
||||
|
||||
filter_by(sha)
|
||||
|
||||
wait_for_requests
|
||||
|
||||
sha_short = sha[0, 7]
|
||||
|
||||
expect(items_count(sha_short)).to be(1)
|
||||
|
||||
item(sha_short).click
|
||||
|
||||
expect(toggle).to have_content sha_short
|
||||
end
|
||||
|
||||
it 'shows no results when there is no branch, tag or commit sha found' do
|
||||
non_existing_ref = 'non_existing_branch_name'
|
||||
|
||||
toggle.click
|
||||
|
||||
filter_by(non_existing_ref)
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(find('.gl-dropdown-contents')).not_to have_content(non_existing_ref)
|
||||
end
|
||||
|
||||
def item(ref_name)
|
||||
find('li', text: ref_name, match: :prefer_exact)
|
||||
end
|
||||
|
||||
def items_count(ref_name)
|
||||
all('li', text: ref_name, match: :prefer_exact).length
|
||||
end
|
||||
|
||||
def filter_by(filter_text)
|
||||
fill_in 'Filter by Git revision', with: filter_text
|
||||
fill_in _('Search by Git revision'), with: filter_text
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -81,11 +81,7 @@ RSpec.describe 'User creates branch', :js, feature_category: :projects do
|
|||
it 'does not create new branch' do
|
||||
invalid_branch_name = '1.0 stable'
|
||||
|
||||
fill_in('branch_name', with: invalid_branch_name)
|
||||
page.find('body').click # defocus the branch_name input
|
||||
|
||||
select_branch('master')
|
||||
click_button('Create branch')
|
||||
create_branch(invalid_branch_name)
|
||||
|
||||
expect(page).to have_content('Branch name is invalid')
|
||||
expect(page).to have_content("can't contain spaces")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
import { GlLabel, GlAvatarsInline } from '@gitlab/ui';
|
||||
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
import ItemMilestone from '~/issuable/components/issue_milestone.vue';
|
||||
import WorkItemLinkChildMetadata from '~/work_items/components/work_item_links/work_item_link_child_metadata.vue';
|
||||
|
||||
import { mockMilestone, mockAssignees, mockLabels } from '../../mock_data';
|
||||
|
||||
describe('WorkItemLinkChildMetadata', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({
|
||||
allowsScopedLabels = true,
|
||||
milestone = mockMilestone,
|
||||
assignees = mockAssignees,
|
||||
labels = mockLabels,
|
||||
} = {}) => {
|
||||
wrapper = shallowMountExtended(WorkItemLinkChildMetadata, {
|
||||
propsData: {
|
||||
allowsScopedLabels,
|
||||
milestone,
|
||||
assignees,
|
||||
labels,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders milestone link button', () => {
|
||||
const milestoneLink = wrapper.findComponent(ItemMilestone);
|
||||
|
||||
expect(milestoneLink.exists()).toBe(true);
|
||||
expect(milestoneLink.props('milestone')).toEqual(mockMilestone);
|
||||
});
|
||||
|
||||
it('renders avatars for assignees', () => {
|
||||
const avatars = wrapper.findComponent(GlAvatarsInline);
|
||||
|
||||
expect(avatars.exists()).toBe(true);
|
||||
expect(avatars.props()).toMatchObject({
|
||||
avatars: mockAssignees,
|
||||
collapsed: true,
|
||||
maxVisible: 2,
|
||||
avatarSize: 24,
|
||||
badgeTooltipProp: 'name',
|
||||
badgeSrOnlyText: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('renders labels', () => {
|
||||
const labels = wrapper.findAllComponents(GlLabel);
|
||||
const mockLabel = mockLabels[0];
|
||||
|
||||
expect(labels).toHaveLength(mockLabels.length);
|
||||
expect(labels.at(0).props()).toMatchObject({
|
||||
title: mockLabel.title,
|
||||
backgroundColor: mockLabel.color,
|
||||
description: mockLabel.description,
|
||||
scoped: false,
|
||||
});
|
||||
expect(labels.at(1).props('scoped')).toBe(true); // Second label is scoped
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { GlButton, GlIcon } from '@gitlab/ui';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
|
|
@ -9,6 +9,7 @@ import { createAlert } from '~/flash';
|
|||
import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.vue';
|
||||
|
||||
import getWorkItemTreeQuery from '~/work_items/graphql/work_item_tree.query.graphql';
|
||||
import WorkItemLinkChildMetadata from '~/work_items/components/work_item_links/work_item_link_child_metadata.vue';
|
||||
import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
|
||||
import WorkItemLinksMenu from '~/work_items/components/work_item_links/work_item_links_menu.vue';
|
||||
import WorkItemTreeChildren from '~/work_items/components/work_item_links/work_item_tree_children.vue';
|
||||
|
|
@ -21,8 +22,12 @@ import {
|
|||
import {
|
||||
workItemTask,
|
||||
workItemObjectiveWithChild,
|
||||
workItemObjectiveNoMetadata,
|
||||
confidentialWorkItemTask,
|
||||
closedWorkItemTask,
|
||||
mockMilestone,
|
||||
mockAssignees,
|
||||
mockLabels,
|
||||
workItemHierarchyTreeResponse,
|
||||
workItemHierarchyTreeFailureResponse,
|
||||
} from '../../mock_data';
|
||||
|
|
@ -101,7 +106,7 @@ describe('WorkItemLinkChild', () => {
|
|||
beforeEach(() => {
|
||||
createComponent();
|
||||
|
||||
titleEl = wrapper.findComponent(GlButton);
|
||||
titleEl = wrapper.findByTestId('item-title');
|
||||
});
|
||||
|
||||
it('renders item title', () => {
|
||||
|
|
@ -129,6 +134,37 @@ describe('WorkItemLinkChild', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('item metadata', () => {
|
||||
const findMetadataComponent = () => wrapper.findComponent(WorkItemLinkChildMetadata);
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
childItem: workItemObjectiveWithChild,
|
||||
workItemType: WORK_ITEM_TYPE_VALUE_OBJECTIVE,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders item metadata component when item has metadata present', () => {
|
||||
const metadataEl = findMetadataComponent();
|
||||
expect(metadataEl.exists()).toBe(true);
|
||||
expect(metadataEl.props()).toMatchObject({
|
||||
allowsScopedLabels: true,
|
||||
milestone: mockMilestone,
|
||||
assignees: mockAssignees,
|
||||
labels: mockLabels,
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render item metadata component when item has no metadata present', () => {
|
||||
createComponent({
|
||||
childItem: workItemObjectiveNoMetadata,
|
||||
workItemType: WORK_ITEM_TYPE_VALUE_OBJECTIVE,
|
||||
});
|
||||
|
||||
expect(findMetadataComponent().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('item menu', () => {
|
||||
let itemMenuEl;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,26 @@
|
|||
import { nextTick } from 'vue';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import WorkItemTree from '~/work_items/components/work_item_links/work_item_tree.vue';
|
||||
import WorkItemLinksForm from '~/work_items/components/work_item_links/work_item_links_form.vue';
|
||||
import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
|
||||
import OkrActionsSplitButton from '~/work_items/components/work_item_links/okr_actions_split_button.vue';
|
||||
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
|
||||
|
||||
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
|
||||
|
||||
import {
|
||||
FORM_TYPES,
|
||||
WORK_ITEM_TYPE_ENUM_OBJECTIVE,
|
||||
WORK_ITEM_TYPE_ENUM_KEY_RESULT,
|
||||
} from '~/work_items/constants';
|
||||
import { childrenWorkItems } from '../../mock_data';
|
||||
import { childrenWorkItems, workItemObjectiveWithChild } from '../../mock_data';
|
||||
|
||||
describe('WorkItemTree', () => {
|
||||
let getWorkItemQueryHandler;
|
||||
let wrapper;
|
||||
|
||||
const findToggleButton = () => wrapper.findByTestId('toggle-tree');
|
||||
|
|
@ -21,10 +30,31 @@ describe('WorkItemTree', () => {
|
|||
const findForm = () => wrapper.findComponent(WorkItemLinksForm);
|
||||
const findWorkItemLinkChildItems = () => wrapper.findAllComponents(WorkItemLinkChild);
|
||||
|
||||
const createComponent = ({ children = childrenWorkItems } = {}) => {
|
||||
Vue.use(VueApollo);
|
||||
|
||||
const createComponent = ({
|
||||
workItemType = 'Objective',
|
||||
children = childrenWorkItems,
|
||||
apolloProvider = null,
|
||||
} = {}) => {
|
||||
const mockWorkItemResponse = {
|
||||
data: {
|
||||
workItem: {
|
||||
...workItemObjectiveWithChild,
|
||||
workItemType: {
|
||||
...workItemObjectiveWithChild.workItemType,
|
||||
name: workItemType,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
getWorkItemQueryHandler = jest.fn().mockResolvedValue(mockWorkItemResponse);
|
||||
|
||||
wrapper = shallowMountExtended(WorkItemTree, {
|
||||
apolloProvider:
|
||||
apolloProvider || createMockApollo([[workItemQuery, getWorkItemQueryHandler]]),
|
||||
propsData: {
|
||||
workItemType: 'Objective',
|
||||
workItemType,
|
||||
workItemId: 'gid://gitlab/WorkItem/515',
|
||||
children,
|
||||
projectPath: 'test/project',
|
||||
|
|
@ -91,4 +121,27 @@ describe('WorkItemTree', () => {
|
|||
|
||||
expect(wrapper.emitted('removeChild')).toEqual([['gid://gitlab/WorkItem/2']]);
|
||||
});
|
||||
|
||||
it.each`
|
||||
description | workItemType | prefetch
|
||||
${'prefetches'} | ${'Issue'} | ${true}
|
||||
${'does not prefetch'} | ${'Objective'} | ${false}
|
||||
`(
|
||||
'$description work-item-link-child on mouseover when workItemType is "$workItemType"',
|
||||
async ({ workItemType, prefetch }) => {
|
||||
createComponent({ workItemType });
|
||||
const firstChild = findWorkItemLinkChildItems().at(0);
|
||||
firstChild.vm.$emit('mouseover', childrenWorkItems[0]);
|
||||
await nextTick();
|
||||
await waitForPromises();
|
||||
|
||||
jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
|
||||
|
||||
if (prefetch) {
|
||||
expect(getWorkItemQueryHandler).toHaveBeenCalled();
|
||||
} else {
|
||||
expect(getWorkItemQueryHandler).not.toHaveBeenCalled();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,6 +36,16 @@ export const mockLabels = [
|
|||
},
|
||||
];
|
||||
|
||||
export const mockMilestone = {
|
||||
__typename: 'Milestone',
|
||||
id: 'gid://gitlab/Milestone/30',
|
||||
title: 'v4.0',
|
||||
state: 'active',
|
||||
expired: false,
|
||||
startDate: '2022-10-17',
|
||||
dueDate: '2022-10-24',
|
||||
};
|
||||
|
||||
export const workItemQueryResponse = {
|
||||
data: {
|
||||
workItem: {
|
||||
|
|
@ -359,11 +369,7 @@ export const workItemResponseFactory = ({
|
|||
? {
|
||||
__typename: 'WorkItemWidgetMilestone',
|
||||
type: 'MILESTONE',
|
||||
milestone: {
|
||||
expired: false,
|
||||
id: 'gid://gitlab/Milestone/30',
|
||||
title: 'v4.0',
|
||||
},
|
||||
milestone: mockMilestone,
|
||||
}
|
||||
: { type: 'MOCK TYPE' },
|
||||
{
|
||||
|
|
@ -937,7 +943,17 @@ export const workItemObjectiveWithChild = {
|
|||
iconName: 'issue-type-objective',
|
||||
__typename: 'WorkItemType',
|
||||
},
|
||||
project: {
|
||||
__typename: 'Project',
|
||||
id: '1',
|
||||
fullPath: 'test-project-path',
|
||||
},
|
||||
userPermissions: {
|
||||
deleteWorkItem: true,
|
||||
updateWorkItem: true,
|
||||
},
|
||||
title: 'Objective',
|
||||
description: 'Objective description',
|
||||
state: 'OPEN',
|
||||
confidential: false,
|
||||
createdAt: '2022-08-03T12:41:54Z',
|
||||
|
|
@ -946,12 +962,51 @@ export const workItemObjectiveWithChild = {
|
|||
{
|
||||
type: 'HIERARCHY',
|
||||
hasChildren: true,
|
||||
parent: null,
|
||||
children: {
|
||||
nodes: [],
|
||||
},
|
||||
__typename: 'WorkItemWidgetHierarchy',
|
||||
},
|
||||
{
|
||||
type: 'MILESTONE',
|
||||
__typename: 'WorkItemWidgetMilestone',
|
||||
milestone: mockMilestone,
|
||||
},
|
||||
{
|
||||
type: 'ASSIGNEES',
|
||||
__typename: 'WorkItemWidgetAssignees',
|
||||
canInviteMembers: true,
|
||||
allowsMultipleAssignees: true,
|
||||
assignees: {
|
||||
__typename: 'UserCoreConnection',
|
||||
nodes: mockAssignees,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'LABELS',
|
||||
__typename: 'WorkItemWidgetLabels',
|
||||
allowsScopedLabels: true,
|
||||
labels: {
|
||||
__typename: 'LabelConnection',
|
||||
nodes: mockLabels,
|
||||
},
|
||||
},
|
||||
],
|
||||
__typename: 'WorkItem',
|
||||
};
|
||||
|
||||
export const workItemObjectiveNoMetadata = {
|
||||
...workItemObjectiveWithChild,
|
||||
widgets: [
|
||||
{
|
||||
type: 'HIERARCHY',
|
||||
hasChildren: true,
|
||||
__typename: 'WorkItemWidgetHierarchy',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const workItemHierarchyTreeResponse = {
|
||||
data: {
|
||||
workItem: {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
|
|||
end_line: end_line,
|
||||
github_id: 1,
|
||||
diff_hunk: diff_hunk,
|
||||
side: 'RIGHT'
|
||||
side: 'RIGHT',
|
||||
discussion_id: discussion_id
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -114,10 +115,6 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
|
|||
.to receive(:database_id)
|
||||
.and_return(merge_request.id)
|
||||
end
|
||||
|
||||
expect(Discussion)
|
||||
.to receive(:discussion_id)
|
||||
.and_return(discussion_id)
|
||||
end
|
||||
|
||||
it_behaves_like 'diff notes without suggestion'
|
||||
|
|
|
|||
|
|
@ -128,64 +128,6 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
|
|||
end
|
||||
end
|
||||
|
||||
describe '#discussion_id' do
|
||||
before do
|
||||
note.project = project
|
||||
note.merge_request = merge_request
|
||||
end
|
||||
|
||||
context 'when the note is a reply to a discussion' do
|
||||
it 'uses the cached value as the discussion_id only when responding an existing discussion' do
|
||||
expect(Discussion)
|
||||
.to receive(:discussion_id)
|
||||
.and_return('FIRST_DISCUSSION_ID', 'SECOND_DISCUSSION_ID')
|
||||
|
||||
# Creates the first discussion id and caches its value
|
||||
expect(note.discussion_id)
|
||||
.to eq('FIRST_DISCUSSION_ID')
|
||||
|
||||
reply_note = described_class.from_json_hash(
|
||||
'note_id' => note.note_id + 1,
|
||||
'in_reply_to_id' => note.note_id
|
||||
)
|
||||
reply_note.project = project
|
||||
reply_note.merge_request = merge_request
|
||||
|
||||
# Reading from the cached value
|
||||
expect(reply_note.discussion_id)
|
||||
.to eq('FIRST_DISCUSSION_ID')
|
||||
|
||||
new_discussion_note = described_class.from_json_hash(
|
||||
'note_id' => note.note_id + 2,
|
||||
'in_reply_to_id' => nil
|
||||
)
|
||||
new_discussion_note.project = project
|
||||
new_discussion_note.merge_request = merge_request
|
||||
|
||||
# Because it's a new discussion, it must not use the cached value
|
||||
expect(new_discussion_note.discussion_id)
|
||||
.to eq('SECOND_DISCUSSION_ID')
|
||||
end
|
||||
|
||||
context 'when cached value does not exist' do
|
||||
it 'falls back to generating a new discussion_id' do
|
||||
expect(Discussion)
|
||||
.to receive(:discussion_id)
|
||||
.and_return('NEW_DISCUSSION_ID')
|
||||
|
||||
reply_note = described_class.from_json_hash(
|
||||
'note_id' => note.note_id + 1,
|
||||
'in_reply_to_id' => note.note_id
|
||||
)
|
||||
reply_note.project = project
|
||||
reply_note.merge_request = merge_request
|
||||
|
||||
expect(reply_note.discussion_id).to eq('NEW_DISCUSSION_ID')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#github_identifiers' do
|
||||
it 'returns a hash with needed identifiers' do
|
||||
expect(note.github_identifiers).to eq(
|
||||
|
|
@ -273,27 +215,40 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
|
|||
end
|
||||
|
||||
describe '.from_api_response' do
|
||||
it_behaves_like 'a DiffNote representation' do
|
||||
let(:response) do
|
||||
{
|
||||
id: note_id,
|
||||
html_url: 'https://github.com/foo/bar/pull/42',
|
||||
path: 'README.md',
|
||||
commit_id: '123abc',
|
||||
original_commit_id: 'original123abc',
|
||||
side: side,
|
||||
user: user_data,
|
||||
diff_hunk: hunk,
|
||||
body: note_body,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at,
|
||||
line: end_line,
|
||||
start_line: start_line,
|
||||
in_reply_to_id: in_reply_to_id
|
||||
}
|
||||
end
|
||||
let(:response) do
|
||||
{
|
||||
id: note_id,
|
||||
html_url: 'https://github.com/foo/bar/pull/42',
|
||||
path: 'README.md',
|
||||
commit_id: '123abc',
|
||||
original_commit_id: 'original123abc',
|
||||
side: side,
|
||||
user: user_data,
|
||||
diff_hunk: hunk,
|
||||
body: note_body,
|
||||
created_at: created_at,
|
||||
updated_at: updated_at,
|
||||
line: end_line,
|
||||
start_line: start_line,
|
||||
in_reply_to_id: in_reply_to_id
|
||||
}
|
||||
end
|
||||
|
||||
subject(:note) { described_class.from_api_response(response) }
|
||||
subject(:note) { described_class.from_api_response(response) }
|
||||
|
||||
it_behaves_like 'a DiffNote representation'
|
||||
|
||||
describe '#discussion_id' do
|
||||
it 'finds or generates discussion_id value' do
|
||||
discussion_id = 'discussion_id'
|
||||
discussion_id_class = Gitlab::GithubImport::Representation::DiffNotes::DiscussionId
|
||||
|
||||
expect_next_instance_of(discussion_id_class, response) do |discussion_id_object|
|
||||
expect(discussion_id_object).to receive(:find_or_generate).and_return(discussion_id)
|
||||
end
|
||||
|
||||
expect(note.discussion_id).to eq(discussion_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -302,6 +257,7 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
|
|||
let(:hash) do
|
||||
{
|
||||
'note_id' => note_id,
|
||||
'html_url' => 'https://github.com/foo/bar/pull/42',
|
||||
'noteable_type' => 'MergeRequest',
|
||||
'noteable_id' => 42,
|
||||
'file_path' => 'README.md',
|
||||
|
|
@ -315,7 +271,8 @@ RSpec.describe Gitlab::GithubImport::Representation::DiffNote, :clean_gitlab_red
|
|||
'updated_at' => updated_at.to_s,
|
||||
'end_line' => end_line,
|
||||
'start_line' => start_line,
|
||||
'in_reply_to_id' => in_reply_to_id
|
||||
'in_reply_to_id' => in_reply_to_id,
|
||||
'discussion_id' => 'FIRST_DISCUSSION_ID'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Representation::DiffNotes::DiscussionId, :clean_gitlab_redis_cache,
|
||||
feature_category: :importers do
|
||||
describe '#discussion_id' do
|
||||
let(:hunk) do
|
||||
'@@ -1 +1 @@
|
||||
-Hello
|
||||
+Hello world'
|
||||
end
|
||||
|
||||
let(:note_id) { 1 }
|
||||
let(:html_url) { 'https://github.com/foo/project_name/pull/42' }
|
||||
let(:note) do
|
||||
{
|
||||
id: note_id,
|
||||
html_url: html_url,
|
||||
path: 'README.md',
|
||||
commit_id: '123abc',
|
||||
original_commit_id: 'original123abc',
|
||||
side: 'RIGHT',
|
||||
user: { id: 4, login: 'alice' },
|
||||
diff_hunk: hunk,
|
||||
body: 'Hello world',
|
||||
created_at: Time.new(2017, 1, 1, 12, 10).utc,
|
||||
updated_at: Time.new(2017, 1, 1, 12, 15).utc,
|
||||
line: 23,
|
||||
start_line: nil,
|
||||
in_reply_to_id: nil
|
||||
}
|
||||
end
|
||||
|
||||
context 'when the note is not a reply to a discussion' do
|
||||
subject(:discussion_id) { described_class.new(note).find_or_generate }
|
||||
|
||||
it 'generates and caches new discussion_id' do
|
||||
expect(Discussion)
|
||||
.to receive(:discussion_id)
|
||||
.and_return('FIRST_DISCUSSION_ID')
|
||||
|
||||
expect(Gitlab::Cache::Import::Caching).to receive(:write).with(
|
||||
"github-importer/discussion-id-map/project_name/42/#{note_id}",
|
||||
'FIRST_DISCUSSION_ID'
|
||||
).and_return('FIRST_DISCUSSION_ID')
|
||||
|
||||
expect(discussion_id).to eq('FIRST_DISCUSSION_ID')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the note is a reply to a discussion' do
|
||||
let(:reply_note) do
|
||||
{
|
||||
note_id: note_id + 1,
|
||||
in_reply_to_id: note_id,
|
||||
html_url: html_url
|
||||
}
|
||||
end
|
||||
|
||||
subject(:discussion_id) { described_class.new(reply_note).find_or_generate }
|
||||
|
||||
it 'uses the cached value as the discussion_id' do
|
||||
expect(Discussion)
|
||||
.to receive(:discussion_id)
|
||||
.and_return('FIRST_DISCUSSION_ID')
|
||||
|
||||
described_class.new(note).find_or_generate
|
||||
|
||||
expect(discussion_id).to eq('FIRST_DISCUSSION_ID')
|
||||
end
|
||||
|
||||
context 'when cached value does not exist' do
|
||||
it 'falls back to generating a new discussion_id' do
|
||||
expect(Discussion)
|
||||
.to receive(:discussion_id)
|
||||
.and_return('NEW_DISCUSSION_ID')
|
||||
|
||||
expect(discussion_id).to eq('NEW_DISCUSSION_ID')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -10,11 +10,12 @@ RSpec.describe UpdateImportSourcesOnApplicationSettings, feature_category: :migr
|
|||
|
||||
describe "#up" do
|
||||
it 'removes google_code and preserves existing valid import sources' do
|
||||
settings.create!(import_sources: import_sources_with_google)
|
||||
record = settings.create!(import_sources: import_sources_with_google.to_yaml)
|
||||
|
||||
migrate!
|
||||
|
||||
expect(YAML.safe_load(ApplicationSetting.last.import_sources)).to eq(import_sources_without_google)
|
||||
expect(record.reload.import_sources).to start_with('---')
|
||||
expect(ApplicationSetting.last.import_sources).to eq(import_sources_without_google)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe FixUpdateImportSourcesOnApplicationSettings, feature_category: :migration do
|
||||
let(:settings) { table(:application_settings) }
|
||||
let(:import_sources) { %w[github git bitbucket bitbucket_server] }
|
||||
|
||||
describe "#up" do
|
||||
shared_examples 'fixes import_sources on application_settings' do
|
||||
it 'ensures YAML is stored' do
|
||||
record = settings.create!(import_sources: data)
|
||||
|
||||
migrate!
|
||||
|
||||
expect(record.reload.import_sources).to start_with('---')
|
||||
expect(ApplicationSetting.last.import_sources).to eq(import_sources)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when import_sources is a String' do
|
||||
let(:data) { import_sources.to_s }
|
||||
|
||||
it_behaves_like 'fixes import_sources on application_settings'
|
||||
end
|
||||
|
||||
context 'when import_sources is already YAML' do
|
||||
let(:data) { import_sources.to_yaml }
|
||||
|
||||
it_behaves_like 'fixes import_sources on application_settings'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -22,10 +22,14 @@ module Spec
|
|||
end
|
||||
|
||||
def select_branch(branch_name)
|
||||
find(".js-branch-select").click
|
||||
ref_selector = '.ref-selector'
|
||||
find(ref_selector).click
|
||||
wait_for_requests
|
||||
|
||||
page.within("#new-branch-form .dropdown-menu") do
|
||||
click_link(branch_name)
|
||||
page.within(ref_selector) do
|
||||
fill_in _('Search by Git revision'), with: branch_name
|
||||
wait_for_requests
|
||||
find('li', text: branch_name, match: :prefer_exact).click
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ RSpec.describe Gitlab::GithubImport::ImportDiffNoteWorker do
|
|||
hash = {
|
||||
'noteable_id' => 42,
|
||||
'github_id' => 42,
|
||||
'html_url' => 'https://github.com/foo/bar/pull/42',
|
||||
'path' => 'README.md',
|
||||
'commit_id' => '123abc',
|
||||
'diff_hunk' => "@@ -1 +1 @@\n-Hello\n+Hello world",
|
||||
|
|
|
|||
Loading…
Reference in New Issue