Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-12-16 09:09:38 +00:00
parent 10476ea7d8
commit bc9b43904d
74 changed files with 1294 additions and 562 deletions

View File

@ -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,
},
});
},
});
}

View File

@ -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();

View File

@ -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'));

View File

@ -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"

View File

@ -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>

View File

@ -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);
}
},
},
};

View File

@ -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'
);

View File

@ -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 = {

View File

@ -2,4 +2,7 @@ fragment MilestoneFragment on Milestone {
expired
id
title
state
startDate
dueDate
}

View File

@ -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
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}
}

View File

@ -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 */
/*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
6a25429104daf2b735f0a22e48dc631ded1aebe7d6f5f9d61520af184f6b5075

View File

@ -0,0 +1 @@
a961cf4e53556fe7899fbabc7bc686d5edaf061abe5a008eb7a6304f64f2f22f

View File

@ -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)));

View File

@ -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

View File

@ -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
---

View File

@ -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
---

View File

@ -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

View File

@ -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.
![Incident timeline event for severity change](img/timeline_event_for_severity_change_v15_6.png)

View File

@ -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.
![Incident List Create](img/incident_list_create_v13_3.png)
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.
![Incident List Create](img/new_incident_create_v13_4.png)
### 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:
![PagerDuty incidents integration](img/pagerduty_incidents_integration_v13_3.png)
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:
![Incident List](img/incident_list_v14_9.png)
- **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).
![Incidents List](img/incident_list_v15_6.png)
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:
![Recent updates view toggle](img/timeline_view_toggle_v14_10.png)
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.

View File

@ -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)**

View File

@ -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)

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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/).

View File

@ -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 | | ✓ | ✓ | ✓ | ✓ |

View File

@ -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

View File

@ -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**.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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")

View File

@ -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
});
});

View File

@ -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;

View File

@ -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();
}
},
);
});

View File

@ -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: {

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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",