Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
58f103ba8f
commit
33a43bde0e
|
@ -1 +1 @@
|
|||
15.1.0
|
||||
15.2.0
|
||||
|
|
2
Gemfile
2
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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#import "../fragments/user.fragment.graphql"
|
||||
|
||||
query getUsersByUsernames($usernames: [String!]) {
|
||||
users(usernames: $usernames) {
|
||||
nodes {
|
||||
...User
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#import "../fragments/user.fragment.graphql"
|
||||
|
||||
query searchAllUsers($search: String!, $first: Int = null) {
|
||||
users(search: $search, first: $first) {
|
||||
nodes {
|
||||
...User
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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'),
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
mutation CreateTimelineEvent($input: TimelineEventCreateInput!) {
|
||||
timelineEventCreate(input: $input) {
|
||||
timelineEvent {
|
||||
id
|
||||
note
|
||||
noteHtml
|
||||
action
|
||||
occurredAt
|
||||
createdAt
|
||||
}
|
||||
errors
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
= form_errors(integration)
|
||||
= form_errors(integration, pajamas_alert: true)
|
||||
|
||||
%div{ data: { testid: "integration-settings-form" } }
|
||||
- if @default_integration
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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**.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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".
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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'],
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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') }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=
|
||||
|
|
Loading…
Reference in New Issue