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:
-rw-r--r--.nvmrc2
-rw-r--r--GITLAB_KAS_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock8
-rw-r--r--app/assets/javascripts/graphql_shared/queries/get_users_by_usernames.query.graphql9
-rw-r--r--app/assets/javascripts/graphql_shared/queries/users_search_all.query.graphql9
-rw-r--r--app/assets/javascripts/issuable/components/related_issuable_item.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/constants.js14
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql13
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql6
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue252
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue35
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/utils.js12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/index.js4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss15
-rw-r--r--app/views/shared/_integration_settings.html.haml2
-rw-r--r--doc/administration/auth/index.md2
-rw-r--r--doc/administration/geo/disaster_recovery/background_verification.md2
-rw-r--r--doc/administration/gitaly/configure_gitaly.md2
-rw-r--r--doc/administration/operations/puma.md2
-rw-r--r--doc/administration/sidekiq.md6
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md2
-rw-r--r--doc/ci/environments/index.md46
-rw-r--r--doc/development/documentation/site_architecture/folder_structure.md9
-rw-r--r--doc/development/licensed_feature_availability.md7
-rw-r--r--doc/user/clusters/agent/install/index.md2
-rw-r--r--doc/user/packages/npm_registry/index.md2
-rw-r--r--locale/gitlab.pot90
-rw-r--r--spec/factories/ci/stages.rb2
-rw-r--r--spec/features/incidents/incident_timeline_events_spec.rb43
-rw-r--r--spec/frontend/issues/show/components/incidents/mock_data.js14
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js141
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js59
-rw-r--r--spec/frontend/issues/show/components/incidents/utils_spec.js25
-rw-r--r--spec/graphql/types/ci/detailed_status_type_spec.rb2
-rw-r--r--spec/graphql/types/ci/status_action_type_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/common_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/stage/factory_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/status/stage/play_manual_spec.rb10
-rw-r--r--spec/lib/gitlab/database/loose_foreign_keys_spec.rb13
-rw-r--r--spec/models/ci/group_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb8
-rw-r--r--spec/models/ci/processable_spec.rb2
-rw-r--r--spec/models/ci/stage_spec.rb28
-rw-r--r--spec/models/commit_status_spec.rb4
-rw-r--r--spec/presenters/ci/stage_presenter_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/job_spec.rb4
-rw-r--r--spec/requests/api/graphql/ci/jobs_spec.rb6
-rw-r--r--spec/requests/api/graphql/ci/pipelines_spec.rb4
-rw-r--r--spec/requests/api/graphql/ci/stages_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/jobs_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/pipeline_spec.rb4
-rw-r--r--spec/serializers/ci/dag_job_group_entity_spec.rb2
-rw-r--r--spec/serializers/ci/dag_pipeline_entity_spec.rb6
-rw-r--r--spec/serializers/ci/dag_stage_entity_spec.rb2
-rw-r--r--spec/serializers/stage_entity_spec.rb8
-rw-r--r--spec/serializers/stage_serializer_spec.rb4
-rw-r--r--spec/services/ci/abort_pipelines_service_spec.rb8
-rw-r--r--spec/services/ci/ensure_stage_service_spec.rb2
-rw-r--r--spec/services/ci/play_manual_stage_service_spec.rb2
-rw-r--r--spec/services/ci/retry_job_service_spec.rb4
-rw-r--r--spec/workers/stage_update_worker_spec.rb2
-rw-r--r--workhorse/go.mod2
-rw-r--r--workhorse/go.sum4
68 files changed, 849 insertions, 155 deletions
diff --git a/.nvmrc b/.nvmrc
index 832d3850644..99cdd8009c3 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-16.14.0
+16.15.0
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index d14dfbac369..6c43fc8aefc 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-15.1.0
+15.2.0
diff --git a/Gemfile b/Gemfile
index 87199a843bf..79c0d742ab1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -93,7 +93,7 @@ gem 'gpgme', '~> 2.0.19'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
-gem 'gitlab_omniauth-ldap', '~> 2.1.1', require: 'omniauth-ldap'
+gem 'gitlab_omniauth-ldap', '~> 2.2.0', require: 'omniauth-ldap'
gem 'net-ldap', '~> 0.16.3'
# API
diff --git a/Gemfile.lock b/Gemfile.lock
index b9b9f440d76..3d0325e4c9d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -549,9 +549,9 @@ GEM
rubocop-rspec (~> 1.44)
gitlab_chronic_duration (0.10.6.2)
numerizer (~> 0.2)
- gitlab_omniauth-ldap (2.1.1)
+ gitlab_omniauth-ldap (2.2.0)
net-ldap (~> 0.16)
- omniauth (~> 1.3)
+ omniauth (>= 1.3, < 3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
globalid (1.0.0)
@@ -1197,7 +1197,7 @@ GEM
ruby2_keywords (0.0.5)
ruby_parser (3.15.0)
sexp_processor (~> 4.9)
- rubyntlm (0.6.2)
+ rubyntlm (0.6.3)
rubypants (0.2.0)
rubyzip (2.3.2)
rugged (1.2.0)
@@ -1573,7 +1573,7 @@ DEPENDENCIES
gitlab-sidekiq-fetcher (= 0.8.0)
gitlab-styles (~> 7.1.0)
gitlab_chronic_duration (~> 0.10.6.2)
- gitlab_omniauth-ldap (~> 2.1.1)
+ gitlab_omniauth-ldap (~> 2.2.0)
gon (~> 6.4.0)
google-api-client (~> 0.33)
google-protobuf (~> 3.19.0)
diff --git a/app/assets/javascripts/graphql_shared/queries/get_users_by_usernames.query.graphql b/app/assets/javascripts/graphql_shared/queries/get_users_by_usernames.query.graphql
new file mode 100644
index 00000000000..07398867544
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/queries/get_users_by_usernames.query.graphql
@@ -0,0 +1,9 @@
+#import "../fragments/user.fragment.graphql"
+
+query getUsersByUsernames($usernames: [String!]) {
+ users(usernames: $usernames) {
+ nodes {
+ ...User
+ }
+ }
+}
diff --git a/app/assets/javascripts/graphql_shared/queries/users_search_all.query.graphql b/app/assets/javascripts/graphql_shared/queries/users_search_all.query.graphql
new file mode 100644
index 00000000000..9c75df84e78
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/queries/users_search_all.query.graphql
@@ -0,0 +1,9 @@
+#import "../fragments/user.fragment.graphql"
+
+query searchAllUsers($search: String!, $first: Int = null) {
+ users(search: $search, first: $first) {
+ nodes {
+ ...User
+ }
+ }
+}
diff --git a/app/assets/javascripts/issuable/components/related_issuable_item.vue b/app/assets/javascripts/issuable/components/related_issuable_item.vue
index e6379b35f7a..a505a988360 100644
--- a/app/assets/javascripts/issuable/components/related_issuable_item.vue
+++ b/app/assets/javascripts/issuable/components/related_issuable_item.vue
@@ -84,7 +84,7 @@ export default {
<gl-icon
v-if="hasState"
ref="iconElementXL"
- class="mr-2 d-block"
+ class="gl-mr-3"
:class="iconClasses"
:name="iconName"
:title="stateTitle"
diff --git a/app/assets/javascripts/issues/show/components/incidents/constants.js b/app/assets/javascripts/issues/show/components/incidents/constants.js
new file mode 100644
index 00000000000..4b855896eaa
--- /dev/null
+++ b/app/assets/javascripts/issues/show/components/incidents/constants.js
@@ -0,0 +1,14 @@
+import { s__ } from '~/locale';
+
+export const timelineTabI18n = Object.freeze({
+ title: s__('Incident|Timeline'),
+ emptyDescription: s__('Incident|No timeline items have been added yet.'),
+ addEventButton: s__('Incident|Add new timeline event'),
+});
+
+export const timelineFormI18n = Object.freeze({
+ createError: s__('Incident|Error creating incident timeline event: %{error}'),
+ areaPlaceholder: s__('Incident|Timeline text...'),
+ saveAndAdd: s__('Incident|Save and add another event'),
+ areaLabel: s__('Incident|Timeline text'),
+});
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
new file mode 100644
index 00000000000..f1fc27dcb2a
--- /dev/null
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql
@@ -0,0 +1,13 @@
+mutation CreateTimelineEvent($input: TimelineEventCreateInput!) {
+ timelineEventCreate(input: $input) {
+ timelineEvent {
+ id
+ note
+ noteHtml
+ action
+ occurredAt
+ createdAt
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
index 7e049d98c1a..bc4e8414bfc 100644
--- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql
@@ -4,17 +4,11 @@ query GetTimelineEvents($fullPath: ID!, $incidentId: IssueID!) {
incidentManagementTimelineEvents(incidentId: $incidentId) {
nodes {
id
- author {
- id
- name
- username
- }
note
noteHtml
action
occurredAt
createdAt
- updatedAt
}
}
}
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
new file mode 100644
index 00000000000..7d510f0accf
--- /dev/null
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
@@ -0,0 +1,252 @@
+<script>
+import { GlDatepicker, GlFormInput, GlFormGroup, GlButton, GlIcon } from '@gitlab/ui';
+import { produce } from 'immer';
+import { sortBy } from 'lodash';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { TYPE_ISSUE } from '~/graphql_shared/constants';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
+import { createAlert } from '~/flash';
+import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
+import { sprintf } from '~/locale';
+import { displayAndLogError, getUtcShiftedDateNow } from './utils';
+import { timelineFormI18n } from './constants';
+
+import CreateTimelineEvent from './graphql/queries/create_timeline_event.mutation.graphql';
+import getTimelineEvents from './graphql/queries/get_timeline_events.query.graphql';
+
+export default {
+ name: 'IncidentTimelineEventForm',
+ restrictedToolBarItems: [
+ 'quote',
+ 'strikethrough',
+ 'bullet-list',
+ 'numbered-list',
+ 'task-list',
+ 'collapsible-section',
+ 'table',
+ 'full-screen',
+ ],
+ components: {
+ MarkdownField,
+ GlDatepicker,
+ GlFormInput,
+ GlFormGroup,
+ GlButton,
+ GlIcon,
+ },
+ i18n: timelineFormI18n,
+ directives: {
+ autofocusonshow,
+ },
+ inject: ['fullPath', 'issuableId'],
+ props: {
+ hasTimelineEvents: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ data() {
+ // Create shifted date to force the datepicker to format in UTC
+ const utcShiftedDate = getUtcShiftedDateNow();
+ return {
+ currentDate: utcShiftedDate,
+ currentHour: utcShiftedDate.getHours(),
+ currentMinute: utcShiftedDate.getMinutes(),
+ timelineText: '',
+ createTimelineEventActive: false,
+ datepickerTextInput: null,
+ };
+ },
+ methods: {
+ hideIncidentTimelineEventForm() {
+ this.$emit('hide-incident-timeline-event-form');
+ },
+ focusDate() {
+ this.$refs.datepicker.$el.focus();
+ },
+ updateCache(store, { data }) {
+ const { timelineEvent: event, errors } = data?.timelineEventCreate || {};
+
+ if (errors.length) {
+ return;
+ }
+
+ const variables = {
+ incidentId: convertToGraphQLId(TYPE_ISSUE, this.issuableId),
+ fullPath: this.fullPath,
+ };
+
+ const sourceData = store.readQuery({
+ query: getTimelineEvents,
+ variables,
+ });
+
+ const newData = produce(sourceData, (draftData) => {
+ const { nodes: draftEventList } = draftData.project.incidentManagementTimelineEvents;
+ draftEventList.push(event);
+ // ISOStrings sort correctly in lexical order
+ const sortedEvents = sortBy(draftEventList, 'occurredAt');
+ draftData.project.incidentManagementTimelineEvents.nodes = sortedEvents;
+ });
+
+ store.writeQuery({
+ query: getTimelineEvents,
+ variables,
+ data: newData,
+ });
+ },
+ createIncidentTimelineEvent(addOneEvent) {
+ this.createTimelineEventActive = true;
+ return this.$apollo
+ .mutate({
+ mutation: CreateTimelineEvent,
+ variables: {
+ input: {
+ incidentId: convertToGraphQLId(TYPE_ISSUE, this.issuableId),
+ note: this.timelineText,
+ occurredAt: this.createDateString(),
+ },
+ },
+ update: this.updateCache,
+ })
+ .then(({ data = {} }) => {
+ const errors = data.timelineEventCreate?.errors;
+ if (errors.length) {
+ createAlert({
+ message: sprintf(this.$options.i18n.createError, { error: errors.join('. ') }, false),
+ });
+ }
+ })
+ .catch(displayAndLogError)
+ .finally(() => {
+ this.createTimelineEventActive = false;
+ this.timelineText = '';
+ if (addOneEvent) {
+ this.hideIncidentTimelineEventForm();
+ }
+ });
+ },
+ createDateString() {
+ const [years, months, days] = this.datepickerTextInput.split('-');
+ const utcDate = new Date(
+ Date.UTC(years, months - 1, days, this.currentHour, this.currentMinute),
+ );
+ return utcDate.toISOString();
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ class="gl-relative gl-display-flex gl-align-items-center"
+ :class="{ 'timeline-entry-vertical-line': hasTimelineEvents }"
+ >
+ <div
+ v-if="hasTimelineEvents"
+ class="gl-display-flex gl-align-items-center gl-justify-content-center gl-align-self-start gl-bg-white gl-text-gray-200 gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-full gl-mt-2 gl-mr-3 gl-w-8 gl-h-8 gl-z-index-1"
+ >
+ <gl-icon name="comment" class="note-icon" />
+ </div>
+ <form class="gl-flex-grow-1" :class="{ 'gl-border-t': hasTimelineEvents }">
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row datetime-picker"
+ >
+ <gl-form-group :label="__('Date')" class="gl-mt-3 gl-mr-3">
+ <gl-datepicker id="incident-date" #default="{ formattedDate }" v-model="currentDate">
+ <gl-form-input
+ id="incident-date"
+ ref="datepicker"
+ v-model="datepickerTextInput"
+ class="gl-datepicker-input gl-pr-7!"
+ :value="formattedDate"
+ :placeholder="__('YYYY-MM-DD')"
+ @keydown.enter="onKeydown"
+ />
+ </gl-datepicker>
+ </gl-form-group>
+ <div class="gl-display-flex gl-mt-3">
+ <gl-form-group :label="__('Time')">
+ <div class="gl-display-flex">
+ <label label-for="timeline-input-hours" class="sr-only"></label>
+ <gl-form-input
+ id="timeline-input-hours"
+ v-model="currentHour"
+ data-testid="input-hours"
+ size="xs"
+ type="number"
+ min="00"
+ max="23"
+ />
+ <label label-for="timeline-input-minutes" class="sr-only"></label>
+ <gl-form-input
+ id="timeline-input-minutes"
+ v-model="currentMinute"
+ class="gl-ml-3"
+ data-testid="input-minutes"
+ size="xs"
+ type="number"
+ min="00"
+ max="59"
+ />
+ </div>
+ </gl-form-group>
+ <p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p>
+ </div>
+ </div>
+ <div class="common-note-form">
+ <gl-form-group :label="$options.i18n.areaLabel">
+ <markdown-field
+ :can-attach-file="false"
+ :add-spacing-classes="false"
+ :show-comment-tool-bar="false"
+ :textarea-value="timelineText"
+ :restricted-tool-bar-items="$options.restrictedToolBarItems"
+ markdown-docs-path=""
+ class="bordered-box gl-mt-0"
+ >
+ <template #textarea>
+ <textarea
+ v-model="timelineText"
+ class="note-textarea js-gfm-input js-autosize markdown-area"
+ dir="auto"
+ data-supports-quick-actions="false"
+ :aria-label="__('Description')"
+ :placeholder="$options.i18n.areaPlaceholder"
+ >
+ </textarea>
+ </template>
+ </markdown-field>
+ </gl-form-group>
+ </div>
+ <gl-form-group class="gl-mb-0">
+ <gl-button
+ variant="confirm"
+ category="primary"
+ class="gl-mr-3"
+ :loading="createTimelineEventActive"
+ @click="createIncidentTimelineEvent(true)"
+ >
+ {{ __('Save') }}
+ </gl-button>
+ <gl-button
+ variant="confirm"
+ category="secondary"
+ class="gl-mr-3 gl-ml-n2"
+ :loading="createTimelineEventActive"
+ @click="createIncidentTimelineEvent(false)"
+ >
+ {{ $options.i18n.saveAndAdd }}
+ </gl-button>
+ <gl-button
+ class="gl-ml-n2"
+ :disabled="createTimelineEventActive"
+ @click="hideIncidentTimelineEventForm"
+ >
+ {{ __('Cancel') }}
+ </gl-button>
+ <div class="gl-border-b gl-pt-5"></div>
+ </gl-form-group>
+ </form>
+ </div>
+</template>
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue
index 400e1f0b725..78ee994aba8 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue
@@ -1,23 +1,29 @@
<script>
-import { GlEmptyState, GlLoadingIcon, GlTab } from '@gitlab/ui';
+import { GlButton, GlEmptyState, GlLoadingIcon, GlTab } from '@gitlab/ui';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_ISSUE } from '~/graphql_shared/constants';
import { fetchPolicies } from '~/lib/graphql';
import getTimelineEvents from './graphql/queries/get_timeline_events.query.graphql';
import { displayAndLogError } from './utils';
+import { timelineTabI18n } from './constants';
+import IncidentTimelineEventForm from './timeline_events_form.vue';
import IncidentTimelineEventsList from './timeline_events_list.vue';
export default {
components: {
+ GlButton,
GlEmptyState,
GlLoadingIcon,
GlTab,
+ IncidentTimelineEventForm,
IncidentTimelineEventsList,
},
- inject: ['fullPath', 'issuableId'],
+ i18n: timelineTabI18n,
+ inject: ['canUpdate', 'fullPath', 'issuableId'],
data() {
return {
+ isEventFormVisible: false,
timelineEvents: [],
};
},
@@ -50,21 +56,42 @@ export default {
return !this.timelineEventLoading && !this.hasTimelineEvents;
},
},
+ methods: {
+ hideEventForm() {
+ this.isEventFormVisible = false;
+ },
+ async showEventForm() {
+ this.isEventFormVisible = true;
+ await this.$nextTick();
+ this.$refs.eventForm.focusDate();
+ },
+ },
};
</script>
<template>
- <gl-tab :title="s__('Incident|Timeline')">
+ <gl-tab :title="$options.i18n.title">
<gl-loading-icon v-if="timelineEventLoading" size="lg" color="dark" class="gl-mt-5" />
<gl-empty-state
v-else-if="showEmptyState"
:compact="true"
- :description="s__('Incident|No timeline items have been added yet.')"
+ :description="$options.i18n.emptyDescription"
/>
<incident-timeline-events-list
v-if="hasTimelineEvents"
:timeline-event-loading="timelineEventLoading"
:timeline-events="timelineEvents"
/>
+ <incident-timeline-event-form
+ v-show="isEventFormVisible"
+ ref="eventForm"
+ :has-timeline-events="hasTimelineEvents"
+ class="timeline-event-note timeline-event-note-form"
+ :class="{ 'gl-pl-0': !hasTimelineEvents }"
+ @hide-incident-timeline-event-form="hideEventForm"
+ />
+ <gl-button v-if="canUpdate" variant="default" class="gl-mb-3 gl-mt-7" @click="showEventForm">
+ {{ $options.i18n.addEventButton }}
+ </gl-button>
</gl-tab>
</template>
diff --git a/app/assets/javascripts/issues/show/components/incidents/utils.js b/app/assets/javascripts/issues/show/components/incidents/utils.js
index d8e5b693b85..256e3025f19 100644
--- a/app/assets/javascripts/issues/show/components/incidents/utils.js
+++ b/app/assets/javascripts/issues/show/components/incidents/utils.js
@@ -18,3 +18,15 @@ const EVENT_ICONS = {
export const getEventIcon = (actionName) => {
return EVENT_ICONS[actionName] ?? EVENT_ICONS.default;
};
+
+/**
+ * Returns a date shifted by the current timezone offset. Allows
+ * date.getHours() and similar to return UTC values.
+ *
+ * @returns {Date}
+ */
+export const getUtcShiftedDateNow = () => {
+ const date = new Date();
+ date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
+ return date;
+};
diff --git a/app/assets/javascripts/vue_merge_request_widget/index.js b/app/assets/javascripts/vue_merge_request_widget/index.js
index dc5c88b1eb3..8d596465970 100644
--- a/app/assets/javascripts/vue_merge_request_widget/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/index.js
@@ -8,10 +8,6 @@ import MrWidgetOptions from 'ee_else_ce/vue_merge_request_widget/mr_widget_optio
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import Translate from '../vue_shared/translate';
-import { registerExtension } from './components/extensions';
-import issuesExtension from './extensions/issues';
-
-registerExtension(issuesExtension);
Vue.use(Translate);
Vue.use(VueApollo);
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index cedb2522c4b..51f964a4b70 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -991,4 +991,19 @@
bottom: -10%;
}
}
+
+ &.timeline-event-note-form {
+ &::before {
+ top: -15% !important; // Override default positioning
+ height: 20%;
+ }
+
+ &::after {
+ content: none;
+ }
+ }
+}
+
+.timeline-event-note-form {
+ padding-left: 20px;
}
diff --git a/app/views/shared/_integration_settings.html.haml b/app/views/shared/_integration_settings.html.haml
index 84710b2ecc7..d58be0f0f4a 100644
--- a/app/views/shared/_integration_settings.html.haml
+++ b/app/views/shared/_integration_settings.html.haml
@@ -1,4 +1,4 @@
-= form_errors(integration)
+= form_errors(integration, pajamas_alert: true)
%div{ data: { testid: "integration-settings-form" } }
- if @default_integration
diff --git a/doc/administration/auth/index.md b/doc/administration/auth/index.md
index 559fe2cb2fe..a412875501e 100644
--- a/doc/administration/auth/index.md
+++ b/doc/administration/auth/index.md
@@ -33,7 +33,7 @@ For more information, see the links shown on this page for each external provide
| **Provider-to-GitLab Role Sync** | SAML Group Sync | LDAP Group Sync<br>SAML Group Sync ([GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/285150) and later) |
| **User Removal** | SCIM (remove user from top-level group) | LDAP (remove user from groups and block from the instance) |
-1. Using Just-In-Time (JIT) provisioning, user accounts are created when the user first signs in.
+1. Using Just-In-Time (JIT) provisioning, user accounts are created when the user first signs in.
## Change apps or configuration
diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md
index c850ce6c6eb..97c9a6c5576 100644
--- a/doc/administration/geo/disaster_recovery/background_verification.md
+++ b/doc/administration/geo/disaster_recovery/background_verification.md
@@ -158,7 +158,7 @@ If the **primary** and **secondary** sites have a checksum verification mismatch
```shell
cd /var/opt/gitlab/git-data/repositories
```
-
+
1. Run the following command on the **primary** site, redirecting the output to a file:
```shell
diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md
index 5bf77de3858..4b2832bebc0 100644
--- a/doc/administration/gitaly/configure_gitaly.md
+++ b/doc/administration/gitaly/configure_gitaly.md
@@ -157,7 +157,7 @@ To configure the _Gitaly token_, edit `/etc/gitlab/gitlab.rb`:
```ruby
gitaly['auth_token'] = 'abc123secret'
```
-
+
There are two ways to configure the _GitLab Shell token_.
Method 1 (recommended):
diff --git a/doc/administration/operations/puma.md b/doc/administration/operations/puma.md
index 66aa6acaa09..cd258104496 100644
--- a/doc/administration/operations/puma.md
+++ b/doc/administration/operations/puma.md
@@ -331,7 +331,7 @@ downtime. Otherwise, skip to the next section.
exit
```
-GDB reports an error if the Puma process terminates before you can run these commands.
+GDB reports an error if the Puma process terminates before you can run these commands.
To buy more time, you can always raise the
Puma worker timeout. For omnibus users, you can edit `/etc/gitlab/gitlab.rb` and
increase it from 60 seconds to 600:
diff --git a/doc/administration/sidekiq.md b/doc/administration/sidekiq.md
index d99e9a0a04b..aa7dfd0dd79 100644
--- a/doc/administration/sidekiq.md
+++ b/doc/administration/sidekiq.md
@@ -13,7 +13,7 @@ PostgreSQL, and Gitaly instances.
By default, GitLab uses UNIX sockets and is not set up to communicate via TCP. To change this:
-1. Edit the `/etc/gitlab/gitlab.rb` file on your GitLab instance and add the following:
+1. Edit the `/etc/gitlab/gitlab.rb` file on your GitLab instance and add the following:
```ruby
@@ -55,7 +55,7 @@ By default, GitLab uses UNIX sockets and is not set up to communicate via TCP. T
```shell
sudo gitlab-ctl reconfigure
```
-
+
1. Restart the `PostgreSQL` server:
```shell
@@ -66,7 +66,7 @@ By default, GitLab uses UNIX sockets and is not set up to communicate via TCP. T
```ruby
gitlab_rails['auto_migrate'] = true
- ```
+ ```
1. Run `reconfigure` again:
diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
index 42babf00841..c536ff59b84 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -22,7 +22,7 @@ To use GitLab CI/CD with a Bitbucket Cloud repository:
- For **Git repository URL**, use the URL from the **Clone this repository** panel in Bitbucket.
- Leave the username blank.
- You can generate and use a [Bitbucket App Password](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/) for the password field.
-
+
GitLab imports the repository and enables [Pull Mirroring](../../user/project/repository/mirror/pull.md).
You can check that mirroring is working in the project by going to **Settings > Repository > Mirroring repositories**.
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index f995d4790c0..6ffa68e4873 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -406,8 +406,11 @@ When you stop an environment:
to the list of **Stopped** environments.
- An [`on_stop` action](../yaml/index.md#environmenton_stop), if defined, is executed.
-Dynamic environments stop automatically when their associated branch is
-deleted.
+There are multiple ways to clean up [dynamic environments](#create-a-dynamic-environment):
+
+- If you use [merge request pipelines](../pipelines/merge_request_pipelines.md), GitLab stops an environment [when a merge request is merged or closed](#stop-an-environment-when-a-merge-request-is-merged-or-closed).
+- If you do _NOT_ use [merge request pipelines](../pipelines/merge_request_pipelines.md), GitLab stops an environment [when the associated feature branch is deleted](#stop-an-environment-when-a-branch-is-deleted).
+- If you set [an expiry period to an environment](../yaml/index.md#environmentauto_stop_in), GitLab stops an environment [when it's expired](#stop-an-environment-after-a-certain-time-period).
#### Stop an environment when a branch is deleted
@@ -425,8 +428,6 @@ deploy_review:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_ENVIRONMENT_SLUG.example.com
on_stop: stop_review
- rules:
- - if: $CI_MERGE_REQUEST_ID
stop_review:
stage: deploy
@@ -435,9 +436,7 @@ stop_review:
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
- rules:
- - if: $CI_MERGE_REQUEST_ID
- when: manual
+ when: manual
```
Both jobs must have the same [`rules`](../yaml/index.md#rules)
@@ -455,6 +454,39 @@ try to check out the code after the branch is deleted.
Read more in the [`.gitlab-ci.yml` reference](../yaml/index.md#environmenton_stop).
+#### Stop an environment when a merge request is merged or closed
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/60885) in GitLab 11.10.
+
+You can configure environments to stop when a merge request is merged or closed.
+This stop trigger is automatically enabled when you use [merge request pipelines](../pipelines/merge_request_pipelines.md).
+
+The following example shows a `deploy_review` job that calls a `stop_review` job
+to clean up and stop the environment.
+
+```yaml
+deploy_review:
+ stage: deploy
+ script:
+ - echo "Deploy a review app"
+ environment:
+ name: review/$CI_COMMIT_REF_SLUG
+ on_stop: stop_review
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+stop_review:
+ stage: deploy
+ script:
+ - echo "Remove review app"
+ environment:
+ name: review/$CI_COMMIT_REF_SLUG
+ action: stop
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+ when: manual
+```
+
#### Stop an environment when another job is finished
You can set an environment to stop when another job is finished.
diff --git a/doc/development/documentation/site_architecture/folder_structure.md b/doc/development/documentation/site_architecture/folder_structure.md
index e960a6491c7..0e8065d794f 100644
--- a/doc/development/documentation/site_architecture/folder_structure.md
+++ b/doc/development/documentation/site_architecture/folder_structure.md
@@ -85,6 +85,15 @@ place for it.
Do not include the same information in multiple places.
[Link to a single source of truth instead.](../styleguide/index.md#link-instead-of-repeating-text)
+For example, if you have code in a repository other than the [primary repositories](index.md#architecture),
+and documentation in the same repository, you can keep the documentation in that repository.
+
+Then you can either:
+
+- Publish it to <https://docs.gitlab.com>.
+- Link to it from <https://docs.gitlab.com> by adding an entry in the global navigation.
+ View [an example](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/fedb6378a3c92274ba3b6031df0d34455594e4cc/content/_data/navigation.yaml#L2944).
+
## References across documents
- Give each folder an `index.md` page that introduces the topic, and both introduces
diff --git a/doc/development/licensed_feature_availability.md b/doc/development/licensed_feature_availability.md
index da5e25b0ec0..21b07ae89b5 100644
--- a/doc/development/licensed_feature_availability.md
+++ b/doc/development/licensed_feature_availability.md
@@ -64,10 +64,9 @@ end
## Allow use of licensed EE features
-To enable plans per namespace turn on the `Allow use of licensed EE features` option from the settings page.
-This will make licensed EE features available to projects only if the project namespace's plan includes the feature
+To enable plans per namespace turn on the `Allow use of licensed EE features` option from the settings page.
+This will make licensed EE features available to projects only if the project namespace's plan includes the feature
or if the project is public. To enable it:
1. If you are developing locally, follow the steps in [simulate SaaS](ee_features.md#act-as-saas) to make the option available.
-1. Visit Admin > Settings > General > "Account and limit" and
-enabling "Allow use of licensed EE features".
+1. Select Admin > Settings > General > "Account and limit" and enable "Allow use of licensed EE features".
diff --git a/doc/user/clusters/agent/install/index.md b/doc/user/clusters/agent/install/index.md
index e039ae83693..4130b2f6221 100644
--- a/doc/user/clusters/agent/install/index.md
+++ b/doc/user/clusters/agent/install/index.md
@@ -156,7 +156,7 @@ To configure your agent, add content to the `config.yaml` file:
- For a GitOps workflow, [view the configuration reference](../gitops.md#gitops-configuration-reference).
- For a GitLab CI/CD workflow, [authorize the agent to access your projects](../ci_cd_workflow.md#authorize-the-agent). Then
- [add `kubectl` commands to your `.gitlab-ci.yml` file](../ci_cd_workflow.md#update-your-gitlab-ciyml-file-to-run-kubectl-commands).
+ [add `kubectl` commands to your `.gitlab-ci.yml` file](../ci_cd_workflow.md#update-your-gitlab-ciyml-file-to-run-kubectl-commands).
## Install multiple agents in your cluster
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index eea7a85dcbf..7ea3c1aa0c8 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -146,7 +146,7 @@ If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
#### Instance-level npm endpoint
NOTE:
-Note: Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project will give you 404 errors. You can use a [personal access token](../../profile/personal_access_tokens.md) as a workaround. [GitLab-#352962](https://gitlab.com/gitlab-org/gitlab/-/issues/352962) proposes a fix to this bug.
+Note: Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project will give you 404 errors. You can use a [personal access token](../../profile/personal_access_tokens.md) as a workaround. [GitLab-#352962](https://gitlab.com/gitlab-org/gitlab/-/issues/352962) proposes a fix to this bug.
To use the [instance-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, set your npm configuration:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e1f6838fc1d..81f7e6967ce 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2732,24 +2732,6 @@ msgstr ""
msgid "AdminSettings|Limit the number of namespaces and projects that can be indexed."
msgstr ""
-msgid "AdminSettings|Max number of repository downloads is not a number"
-msgstr ""
-
-msgid "AdminSettings|Max number of repository downloads must be greater than or equal to 0"
-msgstr ""
-
-msgid "AdminSettings|Max number of repository downloads must be less than or equal to 10000"
-msgstr ""
-
-msgid "AdminSettings|Max number of repository downloads within time period is not a number"
-msgstr ""
-
-msgid "AdminSettings|Max number of repository downloads within time period must be greater than or equal to 0"
-msgstr ""
-
-msgid "AdminSettings|Max number of repository downloads within time period must be less than or equal to 864000"
-msgstr ""
-
msgid "AdminSettings|Maximum duration of a session for Git operations when 2FA is enabled."
msgstr ""
@@ -2783,9 +2765,6 @@ msgstr ""
msgid "AdminSettings|No required pipeline"
msgstr ""
-msgid "AdminSettings|Number of repositories"
-msgstr ""
-
msgid "AdminSettings|Only enable search after installing the plugin, enabling indexing, and recreating the index."
msgstr ""
@@ -2804,9 +2783,6 @@ msgstr ""
msgid "AdminSettings|Registration Features include:"
msgstr ""
-msgid "AdminSettings|Reporting time period (seconds)"
-msgstr ""
-
msgid "AdminSettings|Require users to prove ownership of custom domains"
msgstr ""
@@ -2870,9 +2846,6 @@ msgstr ""
msgid "AdminSettings|The latest artifacts for all jobs in the most recent successful pipelines in each project are stored and do not expire."
msgstr ""
-msgid "AdminSettings|The maximum number of unique repositories a user can download in the specified time period before they're banned."
-msgstr ""
-
msgid "AdminSettings|The projects in this group can be selected as templates for new projects created on the instance. %{link_start}Learn more.%{link_end} "
msgstr ""
@@ -4289,6 +4262,9 @@ msgstr ""
msgid "An error occurred while retrieving projects."
msgstr ""
+msgid "An error occurred while retrieving your settings. Reload the page to try again."
+msgstr ""
+
msgid "An error occurred while saving changes: %{error}"
msgstr ""
@@ -4297,6 +4273,9 @@ msgid_plural "An error occurred while saving the settings"
msgstr[0] ""
msgstr[1] ""
+msgid "An error occurred while saving your settings. Try saving them again."
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
@@ -4504,6 +4483,9 @@ msgstr ""
msgid "Application settings saved successfully"
msgstr ""
+msgid "Application settings saved successfully."
+msgstr ""
+
msgid "Application settings update failed"
msgstr ""
@@ -17371,6 +17353,42 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Excluded users"
+msgstr ""
+
+msgid "GitAbuse|Number of repositories"
+msgstr ""
+
+msgid "GitAbuse|Number of repositories can't be blank. Set to 0 for no limit."
+msgstr ""
+
+msgid "GitAbuse|Number of repositories must be a number."
+msgstr ""
+
+msgid "GitAbuse|Number of repositories should be between %{minNumRepos}-%{maxNumRepos}."
+msgstr ""
+
+msgid "GitAbuse|Reporting time period (seconds)"
+msgstr ""
+
+msgid "GitAbuse|Reporting time period can't be blank. Set to 0 for no limit."
+msgstr ""
+
+msgid "GitAbuse|Reporting time period must be a number."
+msgstr ""
+
+msgid "GitAbuse|Reporting time period should be between %{minTimePeriod}-%{maxTimePeriod} seconds."
+msgstr ""
+
+msgid "GitAbuse|The maximum number of unique repositories a user can download in the specified time period before they're banned."
+msgstr ""
+
+msgid "GitAbuse|Users who are excluded from the Git abuse rate limit."
+msgstr ""
+
+msgid "GitAbuse|You cannot specify more than %{maxExcludedUsers} excluded users."
+msgstr ""
+
msgid "GitHub API rate limit exceeded. Try again after %{reset_time}"
msgstr ""
@@ -20568,6 +20586,9 @@ msgstr ""
msgid "Incidents|Must start with http or https"
msgstr ""
+msgid "Incident|Add new timeline event"
+msgstr ""
+
msgid "Incident|Alert details"
msgstr ""
@@ -20586,12 +20607,18 @@ msgstr ""
msgid "Incident|Editing %{filename}"
msgstr ""
+msgid "Incident|Error creating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
msgid "Incident|No timeline items have been added yet."
msgstr ""
+msgid "Incident|Save and add another event"
+msgstr ""
+
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
@@ -20607,6 +20634,12 @@ msgstr ""
msgid "Incident|Timeline"
msgstr ""
+msgid "Incident|Timeline text"
+msgstr ""
+
+msgid "Incident|Timeline text..."
+msgstr ""
+
msgid "Include author name in notification email body"
msgstr ""
@@ -26109,6 +26142,9 @@ msgstr ""
msgid "No repository"
msgstr ""
+msgid "No results"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb
index 65fca3fc314..41297b01f92 100644
--- a/spec/factories/ci/stages.rb
+++ b/spec/factories/ci/stages.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
FactoryBot.define do
- factory :ci_stage_entity, class: 'Ci::Stage' do
+ factory :ci_stage, class: 'Ci::Stage' do
project factory: :project
pipeline factory: :ci_empty_pipeline
diff --git a/spec/features/incidents/incident_timeline_events_spec.rb b/spec/features/incidents/incident_timeline_events_spec.rb
new file mode 100644
index 00000000000..4424af6a489
--- /dev/null
+++ b/spec/features/incidents/incident_timeline_events_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Incident timeline events', :js do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:incident) { create(:incident, project: project) }
+
+ before_all do
+ project.add_developer(developer)
+ end
+
+ before do
+ stub_feature_flags(incident_timeline: true)
+ sign_in(developer)
+
+ visit project_issues_incident_path(project, incident)
+ wait_for_requests
+ click_link 'Timeline'
+ end
+
+ context 'when add event is clicked' do
+ it 'submits event data when save is clicked' do
+ click_button 'Add new timeline event'
+
+ expect(page).to have_selector('.common-note-form')
+
+ fill_in 'Description', with: 'Event note goes here'
+ fill_in 'timeline-input-hours', with: '07'
+ fill_in 'timeline-input-minutes', with: '25'
+
+ click_button 'Save'
+
+ expect(page).to have_selector('.incident-timeline-events')
+
+ page.within '.timeline-event-note' do
+ expect(page).to have_content('Event note goes here')
+ expect(page).to have_content('07:25')
+ end
+ end
+ end
+end
diff --git a/spec/frontend/issues/show/components/incidents/mock_data.js b/spec/frontend/issues/show/components/incidents/mock_data.js
index b5346a6089a..0c58a44a6bc 100644
--- a/spec/frontend/issues/show/components/incidents/mock_data.js
+++ b/spec/frontend/issues/show/components/incidents/mock_data.js
@@ -70,3 +70,17 @@ export const timelineEventsQueryEmptyResponse = {
},
},
};
+
+export const timelineEventsCreateEventResponse = {
+ timelineEvent: {
+ ...mockEvents[0],
+ },
+ errors: [],
+};
+
+export const timelineEventsCreateEventError = {
+ timelineEvent: {
+ ...mockEvents[0],
+ },
+ errors: ['Error creating timeline event'],
+};
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
new file mode 100644
index 00000000000..2b7f73c0e1c
--- /dev/null
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js
@@ -0,0 +1,141 @@
+import VueApollo from 'vue-apollo';
+import Vue, { nextTick } from 'vue';
+import { GlDatepicker } from '@gitlab/ui';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import IncidentTimelineEventForm from '~/issues/show/components/incidents/timeline_events_form.vue';
+import createTimelineEventMutation from '~/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { createAlert } from '~/flash';
+import { timelineEventsCreateEventResponse, timelineEventsCreateEventError } from './mock_data';
+
+Vue.use(VueApollo);
+
+jest.mock('~/flash');
+
+const addEventResponse = jest.fn().mockResolvedValue(timelineEventsCreateEventResponse);
+
+function createMockApolloProvider(response = addEventResponse) {
+ const requestHandlers = [[createTimelineEventMutation, response]];
+ return createMockApollo(requestHandlers);
+}
+
+describe('Timeline events form', () => {
+ let wrapper;
+
+ const mountComponent = ({ mockApollo, mountMethod = shallowMountExtended, stubs }) => {
+ wrapper = mountMethod(IncidentTimelineEventForm, {
+ propsData: {
+ hasTimelineEvents: true,
+ },
+ provide: {
+ fullPath: 'group/project',
+ issuableId: '1',
+ },
+ apolloProvider: mockApollo,
+ stubs,
+ });
+ };
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ const findSubmitButton = () => wrapper.findByText('Save');
+ const findSubmitAndAddButton = () => wrapper.findByText('Save and add another event');
+ const findCancelButton = () => wrapper.findByText('Cancel');
+ const findDatePicker = () => wrapper.findComponent(GlDatepicker);
+ const findHourInput = () => wrapper.findByTestId('input-hours');
+ const findMinuteInput = () => wrapper.findByTestId('input-minutes');
+
+ const submitForm = async () => {
+ findSubmitButton().trigger('click');
+ await waitForPromises();
+ };
+ const submitFormAndAddAnother = async () => {
+ findSubmitAndAddButton().trigger('click');
+ await waitForPromises();
+ };
+ const cancelForm = async () => {
+ findCancelButton().trigger('click');
+ await waitForPromises();
+ };
+
+ describe('form button behaviour', () => {
+ const closeFormEvent = { 'hide-incident-timeline-event-form': [[]] };
+ beforeEach(() => {
+ mountComponent({ mockApollo: createMockApolloProvider(), mountMethod: mountExtended });
+ });
+
+ it('should close the form on submit', async () => {
+ await submitForm();
+ expect(wrapper.emitted()).toEqual(closeFormEvent);
+ });
+
+ it('should not close the form on "submit and add another"', async () => {
+ await submitFormAndAddAnother();
+ expect(wrapper.emitted()).toEqual({});
+ });
+
+ it('should close the form on cancel', async () => {
+ await cancelForm();
+ expect(wrapper.emitted()).toEqual(closeFormEvent);
+ });
+ });
+
+ describe('addTimelineEventQuery', () => {
+ const expectedData = {
+ input: {
+ incidentId: 'gid://gitlab/Issue/1',
+ note: '',
+ occurredAt: '2020-07-06T00:00:00.000Z',
+ },
+ };
+
+ let mockApollo;
+
+ beforeEach(() => {
+ mockApollo = createMockApolloProvider();
+ mountComponent({ mockApollo, mountMethod: mountExtended });
+ });
+
+ it('should call the mutation with the right variables', async () => {
+ await submitForm();
+
+ expect(addEventResponse).toHaveBeenCalledWith(expectedData);
+ });
+
+ it('should call the mutation with user selected variables', async () => {
+ const expectedUserSelectedData = {
+ input: {
+ ...expectedData.input,
+ occurredAt: '2021-08-12T05:45:00.000Z',
+ },
+ };
+
+ findDatePicker().vm.$emit('input', new Date('2021-08-12'));
+ findHourInput().vm.$emit('input', 5);
+ findMinuteInput().vm.$emit('input', 45);
+
+ await nextTick();
+ await submitForm();
+
+ expect(addEventResponse).toHaveBeenCalledWith(expectedUserSelectedData);
+ });
+ });
+
+ describe('error handling', () => {
+ const mockApollo = createMockApolloProvider(timelineEventsCreateEventError);
+ beforeEach(() => {
+ mountComponent({ mockApollo, mountMethod: mountExtended });
+ });
+
+ it('should show an error when submission fails', async () => {
+ await submitForm();
+
+ expect(createAlert).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
index cf81f4cdf66..993df59c5fa 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
@@ -1,13 +1,15 @@
-import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
import Vue from 'vue';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import TimelineEventsTab from '~/issues/show/components/incidents/timeline_events_tab.vue';
import IncidentTimelineEventsList from '~/issues/show/components/incidents/timeline_events_list.vue';
+import IncidentTimelineEventForm from '~/issues/show/components/incidents/timeline_events_form.vue';
import timelineEventsQuery from '~/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import { createAlert } from '~/flash';
+import { timelineTabI18n } from '~/issues/show/components/incidents/constants';
import { timelineEventsQueryListResponse, timelineEventsQueryEmptyResponse } from './mock_data';
Vue.use(VueApollo);
@@ -28,14 +30,17 @@ describe('TimelineEventsTab', () => {
let wrapper;
const mountComponent = (options = {}) => {
- const { mockApollo, mountMethod = shallowMountExtended } = options;
+ const { mockApollo, mountMethod = shallowMountExtended, stubs, provide } = options;
wrapper = mountMethod(TimelineEventsTab, {
provide: {
fullPath: 'group/project',
issuableId: '1',
+ canUpdate: true,
+ ...provide,
},
apolloProvider: mockApollo,
+ stubs,
});
};
@@ -48,6 +53,8 @@ describe('TimelineEventsTab', () => {
const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findTimelineEventsList = () => wrapper.findComponent(IncidentTimelineEventsList);
+ const findTimelineEventForm = () => wrapper.findComponent(IncidentTimelineEventForm);
+ const findAddEventButton = () => wrapper.findByText(timelineTabI18n.addEventButton);
describe('Timeline events tab', () => {
describe('empty state', () => {
@@ -102,4 +109,50 @@ describe('TimelineEventsTab', () => {
expect(findTimelineEventsList().props('timelineEvents')).toHaveLength(3);
});
});
+
+ describe('add new event form', () => {
+ beforeEach(async () => {
+ mountComponent({
+ mockApollo: createMockApolloProvider(emptyResponse),
+ mountMethod: mountExtended,
+ stubs: {
+ 'incident-timeline-events-list': true,
+ 'gl-tab': true,
+ },
+ });
+ await waitForPromises();
+ });
+
+ it('should show a button when user can update', () => {
+ expect(findAddEventButton().exists()).toBe(true);
+ });
+
+ it('should not show a button when user cannot update', () => {
+ mountComponent({
+ mockApollo: createMockApolloProvider(emptyResponse),
+ provide: { canUpdate: false },
+ });
+
+ expect(findAddEventButton().exists()).toBe(false);
+ });
+
+ it('should not show a form by default', () => {
+ expect(findTimelineEventForm().isVisible()).toBe(false);
+ });
+
+ it('should show a form when button is clicked', async () => {
+ await findAddEventButton().trigger('click');
+
+ expect(findTimelineEventForm().isVisible()).toBe(true);
+ });
+
+ it('should hide the form when the hide event is emitted', async () => {
+ // open the form
+ await findAddEventButton().trigger('click');
+
+ await findTimelineEventForm().vm.$emit('hide-incident-timeline-event-form');
+
+ expect(findTimelineEventForm().isVisible()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/issues/show/components/incidents/utils_spec.js b/spec/frontend/issues/show/components/incidents/utils_spec.js
index ff148702696..0da0114c654 100644
--- a/spec/frontend/issues/show/components/incidents/utils_spec.js
+++ b/spec/frontend/issues/show/components/incidents/utils_spec.js
@@ -1,4 +1,9 @@
-import { displayAndLogError, getEventIcon } from '~/issues/show/components/incidents/utils';
+import timezoneMock from 'timezone-mock';
+import {
+ displayAndLogError,
+ getEventIcon,
+ getUtcShiftedDateNow,
+} from '~/issues/show/components/incidents/utils';
import { createAlert } from '~/flash';
jest.mock('~/flash');
@@ -28,4 +33,22 @@ describe('incident utils', () => {
expect(getEventIcon('non-existent-icon-name')).toBe('comment');
});
});
+
+ describe('getUtcShiftedDateNow', () => {
+ beforeEach(() => {
+ timezoneMock.register('US/Pacific');
+ });
+
+ afterEach(() => {
+ timezoneMock.unregister();
+ });
+
+ it('should shift the date by the timezone offset', () => {
+ const date = new Date();
+
+ const shiftedDate = getUtcShiftedDateNow();
+
+ expect(shiftedDate > date).toBe(true);
+ });
+ });
});
diff --git a/spec/graphql/types/ci/detailed_status_type_spec.rb b/spec/graphql/types/ci/detailed_status_type_spec.rb
index 0c05227aec2..686461cb9a5 100644
--- a/spec/graphql/types/ci/detailed_status_type_spec.rb
+++ b/spec/graphql/types/ci/detailed_status_type_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Types::Ci::DetailedStatusType do
:label, :text, :tooltip, :action)
end
- let_it_be(:stage) { create(:ci_stage_entity, status: :skipped) }
+ let_it_be(:stage) { create(:ci_stage, status: :skipped) }
describe 'id field' do
it 'correctly renders the field' do
diff --git a/spec/graphql/types/ci/status_action_type_spec.rb b/spec/graphql/types/ci/status_action_type_spec.rb
index 4c467bf240e..3f4b52610ae 100644
--- a/spec/graphql/types/ci/status_action_type_spec.rb
+++ b/spec/graphql/types/ci/status_action_type_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe Types::Ci::StatusActionType do
describe 'id field' do
it 'correctly renders the field' do
- stage = build(:ci_stage_entity, status: :skipped)
+ stage = build(:ci_stage, status: :skipped)
status = stage.detailed_status(stage.pipeline.user)
expected_id = "#{stage.class.name}-#{status.id}"
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
index 375841ce236..cbf92f8fa83 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::CreateDeployments do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
- let(:stage) { build(:ci_stage_entity, project: project, statuses: [job]) }
+ let(:stage) { build(:ci_stage, project: project, statuses: [job]) }
let(:pipeline) { create(:ci_pipeline, project: project, stages: [stage]) }
let(:command) do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
index 7b97dde3808..eba0db0adfb 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Create do
context 'tags persistence' do
let(:stage) do
- build(:ci_stage_entity, pipeline: pipeline, project: project)
+ build(:ci_stage, pipeline: pipeline, project: project)
end
let(:job) do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
index 6a7d9b58a05..e07a3ca9033 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
let(:project) { create(:project) }
let(:user) { create(:user) }
- let(:stage) { build(:ci_stage_entity, project: project, statuses: [job]) }
+ let(:stage) { build(:ci_stage, project: project, statuses: [job]) }
let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage]) }
let(:command) do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
index 571455d6279..f14dd70a753 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureResourceGroups do
let(:project) { create(:project) }
let(:user) { create(:user) }
- let(:stage) { build(:ci_stage_entity, project: project, statuses: [job]) }
+ let(:stage) { build(:ci_stage, project: project, statuses: [job]) }
let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage]) }
let!(:environment) { create(:environment, name: 'production', project: project) }
diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb
index a7e0ebf9158..bbd2ce6c83b 100644
--- a/spec/lib/gitlab/ci/status/stage/common_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::Ci::Status::Stage::Common do
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:stage) do
- build(:ci_stage_entity, pipeline: pipeline, name: 'test')
+ build(:ci_stage, pipeline: pipeline, name: 'test')
end
subject do
diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
index 348e0a1eb68..35d44281072 100644
--- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::Ci::Status::Stage::Factory do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
- let(:stage) { create(:ci_stage_entity, pipeline: pipeline) }
+ let(:stage) { create(:ci_stage, pipeline: pipeline) }
subject do
described_class.new(stage, user)
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Ci::Status::Stage::Factory do
context 'when stage has a core status' do
(Ci::HasStatus::AVAILABLE_STATUSES - %w(manual skipped scheduled)).each do |core_status|
context "when core status is #{core_status}" do
- let(:stage) { create(:ci_stage_entity, pipeline: pipeline, status: core_status) }
+ let(:stage) { create(:ci_stage, pipeline: pipeline, status: core_status) }
it "fabricates a core status #{core_status}" do
expect(status).to be_a(
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Ci::Status::Stage::Factory do
context 'when stage has warnings' do
let(:stage) do
- create(:ci_stage_entity, status: :success, pipeline: pipeline)
+ create(:ci_stage, status: :success, pipeline: pipeline)
end
before do
@@ -64,7 +64,7 @@ RSpec.describe Gitlab::Ci::Status::Stage::Factory do
context 'when stage has manual builds' do
(Ci::HasStatus::BLOCKED_STATUS + ['skipped']).each do |core_status|
context "when status is #{core_status}" do
- let(:stage) { create(:ci_stage_entity, pipeline: pipeline, status: core_status) }
+ let(:stage) { create(:ci_stage, pipeline: pipeline, status: core_status) }
it 'fabricates a play manual status' do
expect(status).to be_a(Gitlab::Ci::Status::Stage::PlayManual)
diff --git a/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb b/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb
index 25b79ff2099..9fdaddc083e 100644
--- a/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb
+++ b/spec/lib/gitlab/ci/status/stage/play_manual_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe Gitlab::Ci::Status::Stage::PlayManual do
end
describe '#action_path' do
- let(:stage) { create(:ci_stage_entity, status: 'manual') }
+ let(:stage) { create(:ci_stage, status: 'manual') }
let(:pipeline) { stage.pipeline }
let(:play_manual) { stage.detailed_status(create(:user)) }
@@ -46,25 +46,25 @@ RSpec.describe Gitlab::Ci::Status::Stage::PlayManual do
subject { described_class.matches?(stage, user) }
context 'when stage is skipped' do
- let(:stage) { create(:ci_stage_entity, status: :skipped) }
+ let(:stage) { create(:ci_stage, status: :skipped) }
it { is_expected.to be_truthy }
end
context 'when stage is manual' do
- let(:stage) { create(:ci_stage_entity, status: :manual) }
+ let(:stage) { create(:ci_stage, status: :manual) }
it { is_expected.to be_truthy }
end
context 'when stage is scheduled' do
- let(:stage) { create(:ci_stage_entity, status: :scheduled) }
+ let(:stage) { create(:ci_stage, status: :scheduled) }
it { is_expected.to be_truthy }
end
context 'when stage is success' do
- let(:stage) { create(:ci_stage_entity, status: :success) }
+ let(:stage) { create(:ci_stage, status: :success) }
context 'and does not have manual builds' do
it { is_expected.to be_falsy }
diff --git a/spec/lib/gitlab/database/loose_foreign_keys_spec.rb b/spec/lib/gitlab/database/loose_foreign_keys_spec.rb
index ed11699e494..87a3e0f81e4 100644
--- a/spec/lib/gitlab/database/loose_foreign_keys_spec.rb
+++ b/spec/lib/gitlab/database/loose_foreign_keys_spec.rb
@@ -63,19 +63,22 @@ RSpec.describe Gitlab::Database::LooseForeignKeys do
Gitlab::Database.schemas_to_base_models.fetch(parent_table_schema)
end
- it 'all `to_table` tables are present' do
+ it 'all `to_table` tables are present', :aggregate_failures do
definitions.each do |definition|
base_models_for(definition.to_table).each do |model|
- expect(model.connection).to be_table_exist(definition.to_table)
+ expect(model.connection).to be_table_exist(definition.to_table),
+ "Table #{definition.from_table} does not exist"
end
end
end
- it 'all `from_table` tables are present' do
+ it 'all `from_table` tables are present', :aggregate_failures do
definitions.each do |definition|
base_models_for(definition.from_table).each do |model|
- expect(model.connection).to be_table_exist(definition.from_table)
- expect(model.connection).to be_column_exist(definition.from_table, definition.column)
+ expect(model.connection).to be_table_exist(definition.from_table),
+ "Table #{definition.from_table} does not exist"
+ expect(model.connection).to be_column_exist(definition.from_table, definition.column),
+ "Column #{definition.column} in #{definition.from_table} does not exist"
end
end
end
diff --git a/spec/models/ci/group_spec.rb b/spec/models/ci/group_spec.rb
index 6c96e659a34..4900bc792af 100644
--- a/spec/models/ci/group_spec.rb
+++ b/spec/models/ci/group_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe Ci::Group do
describe '.fabricate' do
let(:pipeline) { create(:ci_empty_pipeline) }
- let(:stage) { create(:ci_stage_entity, pipeline: pipeline) }
+ let(:stage) { create(:ci_stage, pipeline: pipeline) }
before do
create_build(:ci_build, name: 'rspec 0 2')
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 15cb5b42248..e8973de5fd5 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1350,7 +1350,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let(:pipeline) { build(:ci_empty_pipeline, :created) }
before do
- create(:ci_stage_entity, project: project,
+ create(:ci_stage, project: project,
pipeline: pipeline,
position: 4,
name: 'deploy')
@@ -1367,12 +1367,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
stage_idx: 2,
name: 'build')
- create(:ci_stage_entity, project: project,
+ create(:ci_stage, project: project,
pipeline: pipeline,
position: 1,
name: 'sanity')
- create(:ci_stage_entity, project: project,
+ create(:ci_stage, project: project,
pipeline: pipeline,
position: 5,
name: 'cleanup')
@@ -4258,7 +4258,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let(:stage_name) { 'test' }
let(:stage) do
- create(:ci_stage_entity,
+ create(:ci_stage,
pipeline: pipeline,
project: pipeline.project,
name: 'test')
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index 743736236c2..789ae3a2ccc 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Ci::Processable do
new_proc
end
- let_it_be(:stage) { create(:ci_stage_entity, project: project, pipeline: pipeline, name: 'test') }
+ let_it_be(:stage) { create(:ci_stage, project: project, pipeline: pipeline, name: 'test') }
shared_context 'processable bridge' do
let_it_be(:downstream_project) { create(:project, :repository) }
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index b91348eb408..d55a8509a98 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Ci::Stage, :models do
let_it_be(:pipeline) { create(:ci_empty_pipeline) }
- let(:stage) { create(:ci_stage_entity, pipeline: pipeline, project: pipeline.project) }
+ let(:stage) { create(:ci_stage, pipeline: pipeline, project: pipeline.project) }
it_behaves_like 'having unique enum values'
@@ -30,9 +30,9 @@ RSpec.describe Ci::Stage, :models do
describe '.by_position' do
it 'finds stages by position' do
- a = create(:ci_stage_entity, position: 1)
- b = create(:ci_stage_entity, position: 2)
- c = create(:ci_stage_entity, position: 3)
+ a = create(:ci_stage, position: 1)
+ b = create(:ci_stage, position: 2)
+ c = create(:ci_stage, position: 3)
expect(described_class.by_position(1)).to contain_exactly(a)
expect(described_class.by_position(2)).to contain_exactly(b)
@@ -42,9 +42,9 @@ RSpec.describe Ci::Stage, :models do
describe '.by_name' do
it 'finds stages by name' do
- a = create(:ci_stage_entity, name: 'a')
- b = create(:ci_stage_entity, name: 'b')
- c = create(:ci_stage_entity, name: 'c')
+ a = create(:ci_stage, name: 'a')
+ b = create(:ci_stage, name: 'b')
+ c = create(:ci_stage, name: 'c')
expect(described_class.by_name('a')).to contain_exactly(a)
expect(described_class.by_name('b')).to contain_exactly(b)
@@ -54,7 +54,7 @@ RSpec.describe Ci::Stage, :models do
describe '#status' do
context 'when stage is pending' do
- let(:stage) { create(:ci_stage_entity, status: 'pending') }
+ let(:stage) { create(:ci_stage, status: 'pending') }
it 'has a correct status value' do
expect(stage.status).to eq 'pending'
@@ -62,7 +62,7 @@ RSpec.describe Ci::Stage, :models do
end
context 'when stage is success' do
- let(:stage) { create(:ci_stage_entity, status: 'success') }
+ let(:stage) { create(:ci_stage, status: 'success') }
it 'has a correct status value' do
expect(stage.status).to eq 'success'
@@ -119,7 +119,7 @@ RSpec.describe Ci::Stage, :models do
end
context 'when stage has only created builds' do
- let(:stage) { create(:ci_stage_entity, status: :created) }
+ let(:stage) { create(:ci_stage, status: :created) }
before do
create(:ci_build, :created, stage_id: stage.id)
@@ -206,7 +206,7 @@ RSpec.describe Ci::Stage, :models do
using RSpec::Parameterized::TableSyntax
let(:user) { create(:user) }
- let(:stage) { create(:ci_stage_entity, status: :created) }
+ let(:stage) { create(:ci_stage, status: :created) }
subject { stage.detailed_status(user) }
@@ -269,7 +269,7 @@ RSpec.describe Ci::Stage, :models do
describe '#delay' do
subject { stage.delay }
- let(:stage) { create(:ci_stage_entity, status: :created) }
+ let(:stage) { create(:ci_stage, status: :created) }
it 'updates stage status' do
subject
@@ -361,12 +361,12 @@ RSpec.describe Ci::Stage, :models do
end
end
- it_behaves_like 'manual playable stage', :ci_stage_entity
+ it_behaves_like 'manual playable stage', :ci_stage
context 'loose foreign key on ci_stages.project_id' do
it_behaves_like 'cleanup by a loose foreign key' do
let!(:parent) { create(:project) }
- let!(:model) { create(:ci_stage_entity, project: parent) }
+ let!(:model) { create(:ci_stage, project: parent) }
end
end
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 9f58d3c1a80..3cccc41a892 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -803,7 +803,7 @@ RSpec.describe CommitStatus do
describe 'ensure stage assignment' do
context 'when commit status has a stage_id assigned' do
let!(:stage) do
- create(:ci_stage_entity, project: project, pipeline: pipeline)
+ create(:ci_stage, project: project, pipeline: pipeline)
end
let(:commit_status) do
@@ -836,7 +836,7 @@ RSpec.describe CommitStatus do
context 'when commit status does not have stage but it exists' do
let!(:stage) do
- create(:ci_stage_entity, project: project,
+ create(:ci_stage, project: project,
pipeline: pipeline,
name: 'test')
end
diff --git a/spec/presenters/ci/stage_presenter_spec.rb b/spec/presenters/ci/stage_presenter_spec.rb
index 6b82785c2d4..368f03b0150 100644
--- a/spec/presenters/ci/stage_presenter_spec.rb
+++ b/spec/presenters/ci/stage_presenter_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::StagePresenter do
- let(:stage) { create(:ci_stage_entity) }
+ let(:stage) { create(:ci_stage) }
let(:presenter) { described_class.new(stage) }
let!(:build) { create(:ci_build, :tags, :artifacts, pipeline: stage.pipeline, stage: stage.name) }
diff --git a/spec/requests/api/graphql/ci/job_spec.rb b/spec/requests/api/graphql/ci/job_spec.rb
index 2fb90dcd92b..3721155c71b 100644
--- a/spec/requests/api/graphql/ci/job_spec.rb
+++ b/spec/requests/api/graphql/ci/job_spec.rb
@@ -13,8 +13,8 @@ RSpec.describe 'Query.project(fullPath).pipelines.job(id)' do
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
- let_it_be(:prepare_stage) { create(:ci_stage_entity, pipeline: pipeline, project: project, name: 'prepare') }
- let_it_be(:test_stage) { create(:ci_stage_entity, pipeline: pipeline, project: project, name: 'test') }
+ let_it_be(:prepare_stage) { create(:ci_stage, pipeline: pipeline, project: project, name: 'prepare') }
+ let_it_be(:test_stage) { create(:ci_stage, pipeline: pipeline, project: project, name: 'test') }
let_it_be(:job_1) { create(:ci_build, pipeline: pipeline, stage: 'prepare', name: 'Job 1') }
let_it_be(:job_2) { create(:ci_build, pipeline: pipeline, stage: 'test', name: 'Job 2') }
diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb
index d1737fc22ae..8c4ab13fc35 100644
--- a/spec/requests/api/graphql/ci/jobs_spec.rb
+++ b/spec/requests/api/graphql/ci/jobs_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe 'Query.project.pipeline' do
describe '.stages.groups.jobs' do
let(:pipeline) do
pipeline = create(:ci_pipeline, project: project, user: user)
- stage = create(:ci_stage_entity, project: project, pipeline: pipeline, name: 'first', position: 1)
+ stage = create(:ci_stage, project: project, pipeline: pipeline, name: 'first', position: 1)
create(:ci_build, stage_id: stage.id, pipeline: pipeline, name: 'my test job', scheduling_type: :stage)
pipeline
@@ -84,8 +84,8 @@ RSpec.describe 'Query.project.pipeline' do
context 'when there is more than one stage and job needs' do
before do
- build_stage = create(:ci_stage_entity, position: 2, name: 'build', project: project, pipeline: pipeline)
- test_stage = create(:ci_stage_entity, position: 3, name: 'test', project: project, pipeline: pipeline)
+ build_stage = create(:ci_stage, position: 2, name: 'build', project: project, pipeline: pipeline)
+ test_stage = create(:ci_stage, position: 3, name: 'test', project: project, pipeline: pipeline)
create(:ci_build, pipeline: pipeline, name: 'docker 1 2', scheduling_type: :stage, stage: build_stage, stage_idx: build_stage.position)
create(:ci_build, pipeline: pipeline, name: 'docker 2 2', stage: build_stage, stage_idx: build_stage.position, scheduling_type: :dag)
diff --git a/spec/requests/api/graphql/ci/pipelines_spec.rb b/spec/requests/api/graphql/ci/pipelines_spec.rb
index 741af676b6d..a968e5508cb 100644
--- a/spec/requests/api/graphql/ci/pipelines_spec.rb
+++ b/spec/requests/api/graphql/ci/pipelines_spec.rb
@@ -86,8 +86,8 @@ RSpec.describe 'Query.project(fullPath).pipelines' do
describe '.stages' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project) }
- let_it_be(:stage) { create(:ci_stage_entity, pipeline: pipeline, project: project) }
- let_it_be(:other_stage) { create(:ci_stage_entity, pipeline: pipeline, project: project, name: 'other') }
+ let_it_be(:stage) { create(:ci_stage, pipeline: pipeline, project: project) }
+ let_it_be(:other_stage) { create(:ci_stage, pipeline: pipeline, project: project, name: 'other') }
let(:first_n) { var('Int') }
let(:query_path) do
diff --git a/spec/requests/api/graphql/ci/stages_spec.rb b/spec/requests/api/graphql/ci/stages_spec.rb
index 50d2cf75097..1edd6e58486 100644
--- a/spec/requests/api/graphql/ci/stages_spec.rb
+++ b/spec/requests/api/graphql/ci/stages_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'Query.project.pipeline.stages' do
end
before_all do
- create(:ci_stage_entity, pipeline: pipeline, name: 'deploy')
+ create(:ci_stage, pipeline: pipeline, name: 'deploy')
create_list(:ci_build, 2, pipeline: pipeline, stage: 'deploy')
end
diff --git a/spec/requests/api/graphql/project/jobs_spec.rb b/spec/requests/api/graphql/project/jobs_spec.rb
index 1a823ede9ac..7d0eb203d60 100644
--- a/spec/requests/api/graphql/project/jobs_spec.rb
+++ b/spec/requests/api/graphql/project/jobs_spec.rb
@@ -31,8 +31,8 @@ RSpec.describe 'Query.project.jobs' do
end
it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
- build_stage = create(:ci_stage_entity, position: 1, name: 'build', project: project, pipeline: pipeline)
- test_stage = create(:ci_stage_entity, position: 2, name: 'test', project: project, pipeline: pipeline)
+ build_stage = create(:ci_stage, position: 1, name: 'build', project: project, pipeline: pipeline)
+ test_stage = create(:ci_stage, position: 2, name: 'test', project: project, pipeline: pipeline)
create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 1 2', stage: build_stage)
create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 2 2', stage: build_stage)
create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 1 2', stage: test_stage)
diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb
index ccf97918021..1151438b446 100644
--- a/spec/requests/api/graphql/project/pipeline_spec.rb
+++ b/spec/requests/api/graphql/project/pipeline_spec.rb
@@ -290,8 +290,8 @@ RSpec.describe 'getting pipeline information nested in a project' do
end
it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
- build_stage = create(:ci_stage_entity, position: 1, name: 'build', project: project, pipeline: pipeline)
- test_stage = create(:ci_stage_entity, position: 2, name: 'test', project: project, pipeline: pipeline)
+ build_stage = create(:ci_stage, position: 1, name: 'build', project: project, pipeline: pipeline)
+ test_stage = create(:ci_stage, position: 2, name: 'test', project: project, pipeline: pipeline)
create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 1 2', stage: build_stage)
create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 2 2', stage: build_stage)
create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 1 2', stage: test_stage)
diff --git a/spec/serializers/ci/dag_job_group_entity_spec.rb b/spec/serializers/ci/dag_job_group_entity_spec.rb
index d05dd8b05b9..b654b21f583 100644
--- a/spec/serializers/ci/dag_job_group_entity_spec.rb
+++ b/spec/serializers/ci/dag_job_group_entity_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Ci::DagJobGroupEntity do
let_it_be(:request) { double(:request) }
let_it_be(:pipeline) { create(:ci_pipeline) }
- let_it_be(:stage) { create(:ci_stage_entity, pipeline: pipeline) }
+ let_it_be(:stage) { create(:ci_stage, pipeline: pipeline) }
let(:group) { Ci::Group.new(pipeline.project, stage, name: 'test', jobs: jobs) }
let(:entity) { described_class.new(group, request: request) }
diff --git a/spec/serializers/ci/dag_pipeline_entity_spec.rb b/spec/serializers/ci/dag_pipeline_entity_spec.rb
index 31a0dc5c048..548fd247743 100644
--- a/spec/serializers/ci/dag_pipeline_entity_spec.rb
+++ b/spec/serializers/ci/dag_pipeline_entity_spec.rb
@@ -43,9 +43,9 @@ RSpec.describe Ci::DagPipelineEntity do
end
context 'when pipeline has parallel jobs, DAG needs and GenericCommitStatus' do
- let!(:stage_build) { create(:ci_stage_entity, name: 'build', position: 1, pipeline: pipeline) }
- let!(:stage_test) { create(:ci_stage_entity, name: 'test', position: 2, pipeline: pipeline) }
- let!(:stage_deploy) { create(:ci_stage_entity, name: 'deploy', position: 3, pipeline: pipeline) }
+ let!(:stage_build) { create(:ci_stage, name: 'build', position: 1, pipeline: pipeline) }
+ let!(:stage_test) { create(:ci_stage, name: 'test', position: 2, pipeline: pipeline) }
+ let!(:stage_deploy) { create(:ci_stage, name: 'deploy', position: 3, pipeline: pipeline) }
let!(:job_build_1) { create(:ci_build, name: 'build 1', stage: 'build', pipeline: pipeline) }
let!(:job_build_2) { create(:ci_build, name: 'build 2', stage: 'build', pipeline: pipeline) }
diff --git a/spec/serializers/ci/dag_stage_entity_spec.rb b/spec/serializers/ci/dag_stage_entity_spec.rb
index 554437ecef5..3530a5e2bae 100644
--- a/spec/serializers/ci/dag_stage_entity_spec.rb
+++ b/spec/serializers/ci/dag_stage_entity_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Ci::DagStageEntity do
let_it_be(:pipeline) { create(:ci_pipeline) }
let_it_be(:request) { double(:request) }
- let(:stage) { create(:ci_stage_entity, pipeline: pipeline, name: 'test') }
+ let(:stage) { create(:ci_stage, pipeline: pipeline, name: 'test') }
let(:entity) { described_class.new(stage, request: request) }
let!(:job) { create(:ci_build, :success, pipeline: pipeline, stage_id: stage.id) }
diff --git a/spec/serializers/stage_entity_spec.rb b/spec/serializers/stage_entity_spec.rb
index ab74ce76cbd..95d3fd254d4 100644
--- a/spec/serializers/stage_entity_spec.rb
+++ b/spec/serializers/stage_entity_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe StageEntity do
end
let(:stage) do
- create(:ci_stage_entity, pipeline: pipeline, status: :success)
+ create(:ci_stage, pipeline: pipeline, status: :success)
end
before do
@@ -74,7 +74,7 @@ RSpec.describe StageEntity do
end
context 'with a skipped stage ' do
- let(:stage) { create(:ci_stage_entity, status: 'skipped') }
+ let(:stage) { create(:ci_stage, status: 'skipped') }
it 'contains play_all_manual' do
expect(subject[:status][:action]).to be_present
@@ -82,7 +82,7 @@ RSpec.describe StageEntity do
end
context 'with a scheduled stage ' do
- let(:stage) { create(:ci_stage_entity, status: 'scheduled') }
+ let(:stage) { create(:ci_stage, status: 'scheduled') }
it 'contains play_all_manual' do
expect(subject[:status][:action]).to be_present
@@ -90,7 +90,7 @@ RSpec.describe StageEntity do
end
context 'with a manual stage ' do
- let(:stage) { create(:ci_stage_entity, status: 'manual') }
+ let(:stage) { create(:ci_stage, status: 'manual') }
it 'contains play_all_manual' do
expect(subject[:status][:action]).to be_present
diff --git a/spec/serializers/stage_serializer_spec.rb b/spec/serializers/stage_serializer_spec.rb
index 0b5e87dc95b..24e8057375b 100644
--- a/spec/serializers/stage_serializer_spec.rb
+++ b/spec/serializers/stage_serializer_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe StageSerializer do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
- let(:resource) { create(:ci_stage_entity) }
+ let(:resource) { create(:ci_stage) }
let(:serializer) do
described_class.new(current_user: user, project: project)
@@ -21,7 +21,7 @@ RSpec.describe StageSerializer do
end
context 'with an array of entities' do
- let(:resource) { create_list(:ci_stage_entity, 2) }
+ let(:resource) { create_list(:ci_stage, 2) }
it 'serializes the array of pipelines' do
expect(subject).not_to be_empty
diff --git a/spec/services/ci/abort_pipelines_service_spec.rb b/spec/services/ci/abort_pipelines_service_spec.rb
index 9f9519d6829..e43faf0af51 100644
--- a/spec/services/ci/abort_pipelines_service_spec.rb
+++ b/spec/services/ci/abort_pipelines_service_spec.rb
@@ -12,13 +12,13 @@ RSpec.describe Ci::AbortPipelinesService do
let_it_be(:cancelable_build, reload: true) { create(:ci_build, :running, pipeline: cancelable_pipeline) }
let_it_be(:non_cancelable_build, reload: true) { create(:ci_build, :success, pipeline: cancelable_pipeline) }
- let_it_be(:cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageA', status: :running, pipeline: cancelable_pipeline, project: project) }
- let_it_be(:non_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageB', status: :success, pipeline: cancelable_pipeline, project: project) }
+ let_it_be(:cancelable_stage, reload: true) { create(:ci_stage, name: 'stageA', status: :running, pipeline: cancelable_pipeline, project: project) }
+ let_it_be(:non_cancelable_stage, reload: true) { create(:ci_stage, name: 'stageB', status: :success, pipeline: cancelable_pipeline, project: project) }
let_it_be(:manual_pipeline_cancelable_build, reload: true) { create(:ci_build, :created, pipeline: manual_pipeline) }
let_it_be(:manual_pipeline_non_cancelable_build, reload: true) { create(:ci_build, :manual, pipeline: manual_pipeline) }
- let_it_be(:manual_pipeline_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageA', status: :created, pipeline: manual_pipeline, project: project) }
- let_it_be(:manual_pipeline_non_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageB', status: :success, pipeline: manual_pipeline, project: project) }
+ let_it_be(:manual_pipeline_cancelable_stage, reload: true) { create(:ci_stage, name: 'stageA', status: :created, pipeline: manual_pipeline, project: project) }
+ let_it_be(:manual_pipeline_non_cancelable_stage, reload: true) { create(:ci_stage, name: 'stageB', status: :success, pipeline: manual_pipeline, project: project) }
describe '#execute' do
def expect_correct_pipeline_cancellations
diff --git a/spec/services/ci/ensure_stage_service_spec.rb b/spec/services/ci/ensure_stage_service_spec.rb
index 3ede214cdd4..026814edda6 100644
--- a/spec/services/ci/ensure_stage_service_spec.rb
+++ b/spec/services/ci/ensure_stage_service_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Ci::EnsureStageService, '#execute' do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
- let(:stage) { create(:ci_stage_entity) }
+ let(:stage) { create(:ci_stage) }
let(:job) { build(:ci_build) }
let(:service) { described_class.new(project, user) }
diff --git a/spec/services/ci/play_manual_stage_service_spec.rb b/spec/services/ci/play_manual_stage_service_spec.rb
index 3e2a95ee975..b3ae92aa787 100644
--- a/spec/services/ci/play_manual_stage_service_spec.rb
+++ b/spec/services/ci/play_manual_stage_service_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Ci::PlayManualStageService, '#execute' do
let(:stage_status) { 'manual' }
let(:stage) do
- create(:ci_stage_entity,
+ create(:ci_stage,
pipeline: pipeline,
project: project,
name: 'test')
diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb
index acc7a99637b..f042471bd1f 100644
--- a/spec/services/ci/retry_job_service_spec.rb
+++ b/spec/services/ci/retry_job_service_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Ci::RetryJobService do
end
let_it_be(:stage) do
- create(:ci_stage_entity, project: project,
+ create(:ci_stage, project: project,
pipeline: pipeline,
name: 'test')
end
@@ -154,7 +154,7 @@ RSpec.describe Ci::RetryJobService do
end
context 'when the pipeline has other jobs' do
- let!(:stage2) { create(:ci_stage_entity, project: project, pipeline: pipeline, name: 'deploy') }
+ let!(:stage2) { create(:ci_stage, project: project, pipeline: pipeline, name: 'deploy') }
let!(:build2) { create(:ci_build, pipeline: pipeline, stage_id: stage.id ) }
let!(:deploy) { create(:ci_build, pipeline: pipeline, stage_id: stage2.id) }
let!(:deploy_needs_build2) { create(:ci_build_need, build: deploy, name: build2.name) }
diff --git a/spec/workers/stage_update_worker_spec.rb b/spec/workers/stage_update_worker_spec.rb
index 75b324a9e0a..e50c7183153 100644
--- a/spec/workers/stage_update_worker_spec.rb
+++ b/spec/workers/stage_update_worker_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe StageUpdateWorker do
describe '#perform' do
context 'when stage exists' do
- let(:stage) { create(:ci_stage_entity) }
+ let(:stage) { create(:ci_stage) }
it 'updates stage status' do
expect_any_instance_of(Ci::Stage).to receive(:set_status).with('skipped')
diff --git a/workhorse/go.mod b/workhorse/go.mod
index a38ecd06f26..35302f275cc 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.0
- gitlab.com/gitlab-org/gitaly/v15 v15.1.0
+ gitlab.com/gitlab-org/gitaly/v15 v15.1.2
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.15.0
gocloud.dev v0.25.0
diff --git a/workhorse/go.sum b/workhorse/go.sum
index 2861c5b9c0c..9b2d83be892 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -1112,8 +1112,8 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
gitlab.com/gitlab-org/gitaly v1.68.0 h1:VlcJs1+PrhW7lqJUU7Fh1q8FMJujmbbivdfde/cwB98=
gitlab.com/gitlab-org/gitaly v1.68.0/go.mod h1:/pCsB918Zu5wFchZ9hLYin9WkJ2yQqdVNz0zlv5HbXg=
gitlab.com/gitlab-org/gitaly/v14 v14.0.0-rc1/go.mod h1:4Cz8tOAyueSZX5o6gYum1F/unupaOclxqETPcg4ODvQ=
-gitlab.com/gitlab-org/gitaly/v15 v15.1.0 h1:fxnBpIKtaTpkcDUPHKrxuhZj/VWrMqtWRZaO3mwp0WI=
-gitlab.com/gitlab-org/gitaly/v15 v15.1.0/go.mod h1:fZSeTXPLtbmTxNRk9AECM2shtJ6JNzIiLxAgknWBdT4=
+gitlab.com/gitlab-org/gitaly/v15 v15.1.2 h1:6NcpZENpvV4ialIjtrPuxCFHLn6VOrE3mJ9rd/4DuzU=
+gitlab.com/gitlab-org/gitaly/v15 v15.1.2/go.mod h1:fZSeTXPLtbmTxNRk9AECM2shtJ6JNzIiLxAgknWBdT4=
gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc/go.mod h1:5QSTbpAHY2v0iIH5uHh2KA9w7sPUqPmnLjDApI/sv1U=
gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20210720163109-50da611814d2/go.mod h1:QWDYBwuy24qGMandtCngLRPzFgnGPg6LSNoJWPKmJMc=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE=