Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-16 12:09:38 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-16 12:09:38 +0300
commitbc9b43904d491c7b864c8fb6ba06ae0f6625db0d (patch)
treeca0a5ae3bbbf98530707b96a7db08d1bd773b429
parent10476ea7d8253d8462e092ed95aedc01204238bc (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/branches/init_new_branch_ref_selector.js25
-rw-r--r--app/assets/javascripts/new_branch_form.js8
-rw-r--r--app/assets/javascripts/pages/projects/branches/new/index.js7
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue120
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue123
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue15
-rw-r--r--app/assets/javascripts/work_items/components/work_item_type_icon.vue5
-rw-r--r--app/assets/javascripts/work_items/constants.js6
-rw-r--r--app/assets/javascripts/work_items/graphql/milestone.fragment.graphql3
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql29
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql6
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql2
-rw-r--r--app/assets/stylesheets/utilities.scss7
-rw-r--r--app/views/projects/branches/new.html.haml12
-rw-r--r--data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml2
-rw-r--r--data/removals/16_0/source_code-approvals-endpoint.yml2
-rw-r--r--db/migrate/20221209110934_update_import_sources_on_application_settings.rb9
-rw-r--r--db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb25
-rw-r--r--db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb17
-rw-r--r--db/schema_migrations/202212091109351
-rw-r--r--db/schema_migrations/202212121037431
-rw-r--r--db/structure.sql2
-rw-r--r--doc/administration/maintenance_mode/index.md2
-rw-r--r--doc/administration/monitoring/performance/performance_bar.md4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_exporter.md4
-rw-r--r--doc/operations/incident_management/alerts.md23
-rw-r--r--doc/operations/incident_management/img/incident_list_create_v13_3.pngbin21931 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/incident_list_v14_9.pngbin45199 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/incident_list_v15_6.pngbin0 -> 53028 bytes
-rw-r--r--doc/operations/incident_management/img/new_incident_create_v13_4.pngbin12106 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.pngbin13288 -> 0 bytes
-rw-r--r--doc/operations/incident_management/img/timeline_view_toggle_v14_10.pngbin8423 -> 0 bytes
-rw-r--r--doc/operations/incident_management/incident_timeline_events.md2
-rw-r--r--doc/operations/incident_management/incidents.md347
-rw-r--r--doc/operations/incident_management/integrations.md2
-rw-r--r--doc/operations/incident_management/manage_incidents.md263
-rw-r--r--doc/operations/incident_management/paging.md12
-rw-r--r--doc/operations/incident_management/slack.md2
-rw-r--r--doc/operations/metrics/alerts.md55
-rw-r--r--doc/update/deprecations.md2
-rw-r--r--doc/user/permissions.md13
-rw-r--r--doc/user/project/settings/index.md4
-rw-r--r--doc/user/todos.md1
-rw-r--r--lib/gitlab/github_import/importer/diff_note_importer.rb1
-rw-r--r--lib/gitlab/github_import/representation/diff_note.rb44
-rw-r--r--lib/gitlab/github_import/representation/diff_notes/discussion_id.rb57
-rw-r--r--locale/bg/gitlab.po.time_stamp0
-rw-r--r--locale/de/gitlab.po.time_stamp0
-rw-r--r--locale/eo/gitlab.po.time_stamp0
-rw-r--r--locale/es/gitlab.po.time_stamp0
-rw-r--r--locale/fr/gitlab.po.time_stamp0
-rw-r--r--locale/gitlab.pot9
-rw-r--r--locale/it/gitlab.po.time_stamp0
-rw-r--r--locale/ja/gitlab.po.time_stamp0
-rw-r--r--locale/ko/gitlab.po.time_stamp0
-rw-r--r--locale/pt_BR/gitlab.po.time_stamp0
-rw-r--r--locale/ru/gitlab.po.time_stamp0
-rw-r--r--locale/uk/gitlab.po.time_stamp0
-rw-r--r--locale/zh_CN/gitlab.po.time_stamp0
-rw-r--r--locale/zh_HK/gitlab.po.time_stamp0
-rw-r--r--locale/zh_TW/gitlab.po.time_stamp0
-rw-r--r--spec/features/projects/branches/new_branch_ref_dropdown_spec.rb69
-rw-r--r--spec/features/projects/branches/user_creates_branch_spec.rb6
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js67
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js40
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js61
-rw-r--r--spec/frontend/work_items/mock_data.js65
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb7
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_note_spec.rb115
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb84
-rw-r--r--spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb5
-rw-r--r--spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb34
-rw-r--r--spec/support/helpers/features/branches_helpers.rb10
-rw-r--r--spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb1
74 files changed, 1285 insertions, 553 deletions
diff --git a/app/assets/javascripts/branches/init_new_branch_ref_selector.js b/app/assets/javascripts/branches/init_new_branch_ref_selector.js
new file mode 100644
index 00000000000..aad3fbb9982
--- /dev/null
+++ b/app/assets/javascripts/branches/init_new_branch_ref_selector.js
@@ -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,
+ },
+ });
+ },
+ });
+}
diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js
index ef36e58374c..a7c2e572037 100644
--- a/app/assets/javascripts/new_branch_form.js
+++ b/app/assets/javascripts/new_branch_form.js
@@ -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();
diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js
index dbae89b5ade..f2b03468b0b 100644
--- a/app/assets/javascripts/pages/projects/branches/new/index.js
+++ b/app/assets/javascripts/pages/projects/branches/new/index.js
@@ -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'));
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
index b04da53bf89..763f2f338a3 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
@@ -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">
+ <span
+ :id="`stateIcon-${childItem.id}`"
+ class="gl-mr-3"
+ :class="{ 'gl-display-flex': hasMetadata }"
+ data-testid="item-status-icon"
+ >
+ <gl-icon
+ 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
- class="gl-text-secondary"
- :class="iconClass"
- :name="iconName"
- :aria-label="stateTimestampTypeText"
+ 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')"
/>
- </span>
- <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"
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue
new file mode 100644
index 00000000000..7be7e1f3496
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child_metadata.vue
@@ -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>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
index b4bb8a99452..f06de2ca048 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
@@ -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);
+ }
},
},
};
diff --git a/app/assets/javascripts/work_items/components/work_item_type_icon.vue b/app/assets/javascripts/work_items/components/work_item_type_icon.vue
index 96a6493357c..32678e29fa4 100644
--- a/app/assets/javascripts/work_items/components/work_item_type_icon.vue
+++ b/app/assets/javascripts/work_items/components/work_item_type_icon.vue
@@ -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'
);
diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js
index cedebca8190..7fdb84a77ff 100644
--- a/app/assets/javascripts/work_items/constants.js
+++ b/app/assets/javascripts/work_items/constants.js
@@ -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 = {
diff --git a/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql b/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql
index 58140aff89e..5c93370aac9 100644
--- a/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/milestone.fragment.graphql
@@ -2,4 +2,7 @@ fragment MilestoneFragment on Milestone {
expired
id
title
+ state
+ startDate
+ dueDate
}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql
new file mode 100644
index 00000000000..baefcdaea93
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/work_item_metadata_widgets.fragment.graphql
@@ -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
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql b/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
index a850d002de8..006ca29e01c 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
@@ -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
}
}
}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
index a8d4392c1a5..cf3374e1737 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
@@ -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
}
}
}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 0c777ea5d56..714dd932147 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -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 */
/*
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 63d0cf7145d..91efd5ef048 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -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
+
diff --git a/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml b/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml
index 4da311acd41..8cb2a5a0b93 100644
--- a/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml
+++ b/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml
@@ -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
diff --git a/data/removals/16_0/source_code-approvals-endpoint.yml b/data/removals/16_0/source_code-approvals-endpoint.yml
index cd3d87dc960..0d683206f5a 100644
--- a/data/removals/16_0/source_code-approvals-endpoint.yml
+++ b/data/removals/16_0/source_code-approvals-endpoint.yml
@@ -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
diff --git a/db/migrate/20221209110934_update_import_sources_on_application_settings.rb b/db/migrate/20221209110934_update_import_sources_on_application_settings.rb
index ff15b06412f..59955d30544 100644
--- a/db/migrate/20221209110934_update_import_sources_on_application_settings.rb
+++ b/db/migrate/20221209110934_update_import_sources_on_application_settings.rb
@@ -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
diff --git a/db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb b/db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb
new file mode 100644
index 00000000000..d3123113e82
--- /dev/null
+++ b/db/migrate/20221209110935_fix_update_import_sources_on_application_settings.rb
@@ -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
diff --git a/db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb b/db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb
new file mode 100644
index 00000000000..aab67272a77
--- /dev/null
+++ b/db/post_migrate/20221212103743_add_index_id_partition_id_to_ci_build.rb
@@ -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
diff --git a/db/schema_migrations/20221209110935 b/db/schema_migrations/20221209110935
new file mode 100644
index 00000000000..6e39531220d
--- /dev/null
+++ b/db/schema_migrations/20221209110935
@@ -0,0 +1 @@
+6a25429104daf2b735f0a22e48dc631ded1aebe7d6f5f9d61520af184f6b5075 \ No newline at end of file
diff --git a/db/schema_migrations/20221212103743 b/db/schema_migrations/20221212103743
new file mode 100644
index 00000000000..7f225c9516f
--- /dev/null
+++ b/db/schema_migrations/20221212103743
@@ -0,0 +1 @@
+a961cf4e53556fe7899fbabc7bc686d5edaf061abe5a008eb7a6304f64f2f22f \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index cae57e8aae2..754d9796403 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -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)));
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index 0b4488075d0..c30465b4df9 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -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
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index de6178a3151..171a441eaec 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -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
---
diff --git a/doc/administration/monitoring/prometheus/gitlab_exporter.md b/doc/administration/monitoring/prometheus/gitlab_exporter.md
index bce35306505..1f4156349c5 100644
--- a/doc/administration/monitoring/prometheus/gitlab_exporter.md
+++ b/doc/administration/monitoring/prometheus/gitlab_exporter.md
@@ -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
---
diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md
index 69b74127891..0dffa15351c 100644
--- a/doc/operations/incident_management/alerts.md
+++ b/doc/operations/incident_management/alerts.md
@@ -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.
diff --git a/doc/operations/incident_management/img/incident_list_create_v13_3.png b/doc/operations/incident_management/img/incident_list_create_v13_3.png
deleted file mode 100644
index a000c849099..00000000000
--- a/doc/operations/incident_management/img/incident_list_create_v13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/incident_list_v14_9.png b/doc/operations/incident_management/img/incident_list_v14_9.png
deleted file mode 100644
index 4cf6f0e6522..00000000000
--- a/doc/operations/incident_management/img/incident_list_v14_9.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/incident_list_v15_6.png b/doc/operations/incident_management/img/incident_list_v15_6.png
new file mode 100644
index 00000000000..fe2a91e2eba
--- /dev/null
+++ b/doc/operations/incident_management/img/incident_list_v15_6.png
Binary files differ
diff --git a/doc/operations/incident_management/img/new_incident_create_v13_4.png b/doc/operations/incident_management/img/new_incident_create_v13_4.png
deleted file mode 100644
index 458532736bd..00000000000
--- a/doc/operations/incident_management/img/new_incident_create_v13_4.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.png b/doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.png
deleted file mode 100644
index 08a45d001c2..00000000000
--- a/doc/operations/incident_management/img/pagerduty_incidents_integration_v13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/img/timeline_view_toggle_v14_10.png b/doc/operations/incident_management/img/timeline_view_toggle_v14_10.png
deleted file mode 100644
index 90f1d205fed..00000000000
--- a/doc/operations/incident_management/img/timeline_view_toggle_v14_10.png
+++ /dev/null
Binary files differ
diff --git a/doc/operations/incident_management/incident_timeline_events.md b/doc/operations/incident_management/incident_timeline_events.md
index 897fcb9efc6..4af5a815929 100644
--- a/doc/operations/incident_management/incident_timeline_events.md
+++ b/doc/operations/incident_management/incident_timeline_events.md
@@ -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)
diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md
index de9b7d63369..5cfb8a77fc9 100644
--- a/doc/operations/incident_management/incidents.md
+++ b/doc/operations/incident_management/incidents.md
@@ -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**.
+- **State**: To filter incidents by their state, select **Open**, **Closed**,
+ or **All** above the incident list.
+- **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:
-### Create incidents via the PagerDuty webhook
+ - **{severity-critical}** Critical - S1
+ - **{severity-high}** High - S2
+ - **{severity-medium}** Medium - S3
+ - **{severity-low}** Low - S4
+ - **{severity-unknown}** Unknown
-> - [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.
+- **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:
-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:
+ - Triggered
+ - Acknowledged
+ - Resolved
-Prerequisites:
+ In the Premium or Ultimate tier, this field is also linked to [on-call escalation](paging.md#escalating-an-incident) for the incident.
-- You must have at least the Maintainer role for the project.
+- **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).
-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:
+![Incidents List](img/incident_list_v15_6.png)
- ![PagerDuty incidents integration](img/pagerduty_incidents_integration_v13_3.png)
+For an example of the incident list in action, visit this
+[demo project](https://gitlab.com/gitlab-org/monitor/monitor-sandbox/-/incidents).
-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.
+### Sort the incident list
-To confirm the integration is successful, trigger a test incident from PagerDuty to
-confirm that a GitLab incident is created from the incident.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) in GitLab 13.3: incidents are sorted by created date by default.
-## Incident list
+The incident list shows incidents sorted by incident created date, showing the newest first.
-Whether you can view an incident depends on the [project visibility level](../../user/public_access.md) and
-the incident's confidentiality status:
+To sort by another column, or to change the sorting order, select the column.
-- 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 columns you can sort by:
-The Incident list is available at **Monitor > Incidents**
-in your project's sidebar. The list contains the following metrics:
+- Severity
+- Status
+- Time to SLA
+- Published
-![Incident List](img/incident_list_v14_9.png)
+### Filter the incidents list
-- **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
- 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.
-
-- **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**
-
- In GitLab Premium, this field is also linked to [on-call escalation](paging.md#escalating-an-incident) for the incident.
-
-- **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).
-
-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.
-
-Incidents share the [Issues API](../../api/issues.md).
-
-NOTE:
-For a live example of the incident list in action, visit this
-[demo project](https://gitlab.com/gitlab-examples/ops/incident-setup/everyone/tanuki-inc/-/incidents).
+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.
diff --git a/doc/operations/incident_management/integrations.md b/doc/operations/incident_management/integrations.md
index d0d4ee6e498..fdcfbe0cb2c 100644
--- a/doc/operations/incident_management/integrations.md
+++ b/doc/operations/incident_management/integrations.md
@@ -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)**
diff --git a/doc/operations/incident_management/manage_incidents.md b/doc/operations/incident_management/manage_incidents.md
new file mode 100644
index 00000000000..75826c2c55e
--- /dev/null
+++ b/doc/operations/incident_management/manage_incidents.md
@@ -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)
diff --git a/doc/operations/incident_management/paging.md b/doc/operations/incident_management/paging.md
index 9ce87d03686..70c67977b73 100644
--- a/doc/operations/incident_management/paging.md
+++ b/doc/operations/incident_management/paging.md
@@ -54,12 +54,14 @@ or stop alert escalations by [updating the alert's status](alerts.md#change-an-a
> - [Feature flag `incident_escalations`](https://gitlab.com/gitlab-org/gitlab/-/issues/345769) removed in GitLab 15.1.
For incidents, paging on-call responders is optional for each individual incident.
-To begin escalating the incident, [set the incident's escalation policy](incidents.md#change-escalation-policy).
+
+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. 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).
+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](alerts.md#create-an-incident-from-an-alert)
+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.
diff --git a/doc/operations/incident_management/slack.md b/doc/operations/incident_management/slack.md
index 37cfb790d70..1ab1391ea2a 100644
--- a/doc/operations/incident_management/slack.md
+++ b/doc/operations/incident_management/slack.md
@@ -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.
diff --git a/doc/operations/metrics/alerts.md b/doc/operations/metrics/alerts.md
index aa46384688b..4b6bc91cc73 100644
--- a/doc/operations/metrics/alerts.md
+++ b/doc/operations/metrics/alerts.md
@@ -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.
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index 43e7c9dac37..ed49a4ade09 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -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/).
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index cf4a13d102b..f3702b848fa 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -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 | | ✓ | ✓ | ✓ | ✓ |
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 45f84eee4a7..a88427ab20b 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -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
diff --git a/doc/user/todos.md b/doc/user/todos.md
index 9279aee25ff..4102d31cfc4 100644
--- a/doc/user/todos.md
+++ b/doc/user/todos.md
@@ -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**.
diff --git a/lib/gitlab/github_import/importer/diff_note_importer.rb b/lib/gitlab/github_import/importer/diff_note_importer.rb
index fff6552372b..44ffcd7a1e4 100644
--- a/lib/gitlab/github_import/importer/diff_note_importer.rb
+++ b/lib/gitlab/github_import/importer/diff_note_importer.rb
@@ -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
diff --git a/lib/gitlab/github_import/representation/diff_note.rb b/lib/gitlab/github_import/representation/diff_note.rb
index f3be90834c7..9259d0295d5 100644
--- a/lib/gitlab/github_import/representation/diff_note.rb
+++ b/lib/gitlab/github_import/representation/diff_note.rb
@@ -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
diff --git a/lib/gitlab/github_import/representation/diff_notes/discussion_id.rb b/lib/gitlab/github_import/representation/diff_notes/discussion_id.rb
new file mode 100644
index 00000000000..38b560f21c0
--- /dev/null
+++ b/lib/gitlab/github_import/representation/diff_notes/discussion_id.rb
@@ -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
diff --git a/locale/bg/gitlab.po.time_stamp b/locale/bg/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/bg/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/de/gitlab.po.time_stamp b/locale/de/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/de/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/eo/gitlab.po.time_stamp b/locale/eo/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/eo/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/es/gitlab.po.time_stamp b/locale/es/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/es/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/fr/gitlab.po.time_stamp b/locale/fr/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/fr/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c60e091568b..5345dc4bd0f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/locale/it/gitlab.po.time_stamp b/locale/it/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/it/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/ja/gitlab.po.time_stamp b/locale/ja/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/ja/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/ko/gitlab.po.time_stamp b/locale/ko/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/ko/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/pt_BR/gitlab.po.time_stamp b/locale/pt_BR/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/pt_BR/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/ru/gitlab.po.time_stamp b/locale/ru/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/ru/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/uk/gitlab.po.time_stamp b/locale/uk/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/uk/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/zh_CN/gitlab.po.time_stamp b/locale/zh_CN/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/zh_CN/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/zh_HK/gitlab.po.time_stamp b/locale/zh_HK/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/zh_HK/gitlab.po.time_stamp
+++ /dev/null
diff --git a/locale/zh_TW/gitlab.po.time_stamp b/locale/zh_TW/gitlab.po.time_stamp
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/locale/zh_TW/gitlab.po.time_stamp
+++ /dev/null
diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
index fb68195362a..eb370cfc1fc 100644
--- a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
+++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
@@ -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)
+
+ wait_for_requests
+
+ expect(items_count(tag_name)).to be(1)
- expect(items_count).to be(1)
+ item(tag_name).click
- filter_by('video')
+ expect(toggle).to have_content tag_name
+ end
+
+ it 'finds a branch in a list' do
+ branch_name = 'audio'
- expect(items_count).to be(1)
+ toggle.click
- find('.create-from .dropdown-content li').click
+ filter_by(branch_name)
- expect(toggle).to have_content 'video'
+ wait_for_requests
+
+ expect(items_count(branch_name)).to be(1)
+
+ item(branch_name).click
+
+ expect(toggle).to have_content branch_name
end
- it 'accepts a manually entered commit SHA' do
+ it 'finds a commit in a list' do
toggle.click
- filter_by('somecommitsha')
+ filter_by(sha)
+
+ wait_for_requests
+
+ sha_short = sha[0, 7]
- find('.create-from input[type=search]').send_keys(:enter)
+ 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
- expect(toggle).to have_content 'somecommitsha'
+ def item(ref_name)
+ find('li', text: ref_name, match: :prefer_exact)
end
- def items_count
- all('.create-from .dropdown-content li').length
+ 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
diff --git a/spec/features/projects/branches/user_creates_branch_spec.rb b/spec/features/projects/branches/user_creates_branch_spec.rb
index bf7669e9d0c..60bd77393e9 100644
--- a/spec/features/projects/branches/user_creates_branch_spec.rb
+++ b/spec/features/projects/branches/user_creates_branch_spec.rb
@@ -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")
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js
new file mode 100644
index 00000000000..47489d4796b
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_metadata_spec.js
@@ -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
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
index 3a8e785bc80..73d498ad055 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
@@ -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;
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
index cc2e174dfda..96211e12755 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_tree_spec.js
@@ -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();
+ }
+ },
+ );
});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index ed9c3f906b9..f7301701dc3 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -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: {
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index 1bcb7fec327..73ba49bf4ed 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -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'
diff --git a/spec/lib/gitlab/github_import/representation/diff_note_spec.rb b/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
index a656cd0d056..56fabe854f9 100644
--- a/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/diff_note_spec.rb
@@ -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) }
+
+ 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
- subject(:note) { described_class.from_api_response(response) }
+ 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
diff --git a/spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb b/spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb
new file mode 100644
index 00000000000..64a16516e07
--- /dev/null
+++ b/spec/lib/gitlab/github_import/representation/diff_notes/discussion_id_spec.rb
@@ -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
diff --git a/spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb b/spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb
index 6c6121c70e4..899074399a1 100644
--- a/spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb
+++ b/spec/migrations/20221209110934_update_import_sources_on_application_settings_spec.rb
@@ -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
diff --git a/spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb b/spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb
new file mode 100644
index 00000000000..e5b20b2d48a
--- /dev/null
+++ b/spec/migrations/20221209110935_fix_update_import_sources_on_application_settings_spec.rb
@@ -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
diff --git a/spec/support/helpers/features/branches_helpers.rb b/spec/support/helpers/features/branches_helpers.rb
index 2a50b41cb4e..d4f96718cc0 100644
--- a/spec/support/helpers/features/branches_helpers.rb
+++ b/spec/support/helpers/features/branches_helpers.rb
@@ -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
diff --git a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
index 15bc55c1526..c92741e8f10 100644
--- a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb
@@ -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",