提交 7f305b57 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 04edf654
......@@ -355,7 +355,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.7.0'
gem 'factory_bot_rails', '~> 5.1.0'
gem 'rspec-rails', '~> 4.0.0.beta4'
gem 'rspec-rails', '~> 4.0.0'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.11.0'
......
......@@ -614,7 +614,7 @@ GEM
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.4.0)
loofah (2.5.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
lru_redux (1.1.0)
......@@ -906,7 +906,7 @@ GEM
rspec-mocks (~> 3.9.0)
rspec-core (3.9.1)
rspec-support (~> 3.9.1)
rspec-expectations (3.9.0)
rspec-expectations (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
......@@ -918,7 +918,7 @@ GEM
proc_to_ast
rspec (>= 2.13, < 4)
unparser
rspec-rails (4.0.0.beta4)
rspec-rails (4.0.0)
actionpack (>= 4.2)
activesupport (>= 4.2)
railties (>= 4.2)
......@@ -1078,7 +1078,7 @@ GEM
truncato (0.7.11)
htmlentities (~> 4.3.1)
nokogiri (>= 1.7.0, <= 2.0)
tzinfo (1.2.6)
tzinfo (1.2.7)
thread_safe (~> 0.1)
u2f (0.2.1)
uber (0.1.0)
......@@ -1357,7 +1357,7 @@ DEPENDENCIES
rouge (~> 3.18.0)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 4.0.0.beta4)
rspec-rails (~> 4.0.0)
rspec-retry (~> 0.6.1)
rspec_junit_formatter
rspec_profiling (~> 0.0.5)
......
......@@ -113,9 +113,6 @@ export default Vue.extend({
// eslint-disable-next-line @gitlab/require-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
watch: {
filter: {
......
......@@ -113,9 +113,6 @@ export default {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
watch: {
filter: {
......@@ -286,7 +283,6 @@ export default {
:background-color="list.label.color"
:description="list.label.description"
:scoped="showScopedLabels(list.label)"
:scoped-labels-documentation-link="helpLink"
:size="!list.isExpanded ? 'sm' : ''"
:title="list.label.title"
tooltip-placement="bottom"
......
......@@ -58,11 +58,6 @@ export default {
required: false,
default: false,
},
scopedLabelsDocumentationLink: {
type: String,
required: false,
default: '#',
},
},
data() {
return {
......@@ -208,7 +203,6 @@ export default {
:can-admin-board="canAdminBoard"
:milestone-path="milestonePath"
:labels-path="labelsPath"
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
:enable-scoped-labels="enableScopedLabels"
:project-id="projectId"
:group-id="groupId"
......
......@@ -70,9 +70,6 @@ export default Vue.extend({
selectedLabels() {
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
watch: {
detail: {
......
......@@ -86,11 +86,6 @@ export default {
required: false,
default: false,
},
scopedLabelsDocumentationLink: {
type: String,
required: false,
default: '#',
},
},
data() {
return {
......@@ -348,7 +343,6 @@ export default {
:scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
:weights="weights"
:enable-scoped-labels="enabledScopedLabels"
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
/>
</span>
</div>
......
......@@ -102,9 +102,6 @@ export default {
orderedLabels() {
return sortBy(this.issue.labels.filter(this.isNonListLabel), 'title');
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
methods: {
isIndexLessThanlimit(index) {
......@@ -181,7 +178,6 @@ export default {
:description="label.description"
size="sm"
:scoped="showScopedLabel(label)"
:scoped-labels-documentation-link="helpLink"
@click="filterByLabel(label)"
/>
</template>
......
......@@ -28,7 +28,6 @@ const boardsStore = {
limitToHours: false,
},
scopedLabels: {
helpLink: '',
enabled: false,
},
filter: {
......
......@@ -24,8 +24,8 @@ export default {
discardModalTitle() {
return sprintf(__('Discard changes to %{path}?'), { path: this.activeFile.path });
},
isStaged() {
return !this.activeFile.changed && this.activeFile.staged;
canDiscard() {
return this.activeFile.changed || this.activeFile.staged;
},
},
methods: {
......@@ -53,7 +53,7 @@ export default {
<changed-file-icon :file="activeFile" :is-centered="false" />
<div class="ml-auto">
<button
v-if="!isStaged"
v-if="canDiscard"
ref="discardButton"
type="button"
class="btn btn-remove btn-inverted append-right-8"
......
<script>
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { GlAlert, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import last from 'lodash/last';
import { __ } from '~/locale';
import getJiraImportDetailsQuery from '../queries/get_jira_import_details.query.graphql';
import initiateJiraImportMutation from '../queries/initiate_jira_import.mutation.graphql';
......@@ -13,6 +14,7 @@ export default {
components: {
GlAlert,
GlLoadingIcon,
GlSprintf,
JiraImportForm,
JiraImportProgress,
JiraImportSetup,
......@@ -51,6 +53,7 @@ export default {
return {
errorMessage: '',
showAlert: false,
selectedProject: undefined,
};
},
apollo: {
......@@ -63,7 +66,7 @@ export default {
},
update: ({ project }) => ({
status: project.jiraImportStatus,
import: project.jiraImports.nodes[0],
imports: project.jiraImports.nodes,
}),
skip() {
return !this.isJiraConfigured;
......@@ -77,6 +80,24 @@ export default {
jiraProjectsOptions() {
return this.jiraProjects.map(([text, value]) => ({ text, value }));
},
mostRecentImport() {
// The backend returns JiraImports ordered by created_at asc in app/models/project.rb
return last(this.jiraImportDetails?.imports);
},
numberOfPreviousImportsForProject() {
return this.jiraImportDetails?.imports?.reduce?.(
(acc, jiraProject) => (jiraProject.jiraProjectKey === this.selectedProject ? acc + 1 : acc),
0,
);
},
importLabel() {
return this.selectedProject
? `jira-import::${this.selectedProject}-${this.numberOfPreviousImportsForProject + 1}`
: 'jira-import::KEY-1';
},
hasPreviousImports() {
return this.numberOfPreviousImportsForProject > 0;
},
},
methods: {
dismissAlert() {
......@@ -97,6 +118,13 @@ export default {
return;
}
const cacheData = store.readQuery({
query: getJiraImportDetailsQuery,
variables: {
fullPath: this.projectPath,
},
});
store.writeQuery({
query: getJiraImportDetailsQuery,
variables: {
......@@ -106,7 +134,10 @@ export default {
project: {
jiraImportStatus: IMPORT_STATE.SCHEDULED,
jiraImports: {
nodes: [data.jiraImportStart.jiraImport],
nodes: [
...cacheData.project.jiraImports.nodes,
data.jiraImportStart.jiraImport,
],
__typename: 'JiraImportConnection',
},
// eslint-disable-next-line @gitlab/require-i18n-strings
......@@ -119,6 +150,8 @@ export default {
.then(({ data }) => {
if (data.jiraImportStart.errors.length) {
this.setAlertMessage(data.jiraImportStart.errors.join('. '));
} else {
this.selectedProject = undefined;
}
})
.catch(() => this.setAlertMessage(__('There was an error importing the Jira project.')));
......@@ -136,6 +169,19 @@ export default {
<gl-alert v-if="showAlert" variant="danger" @dismiss="dismissAlert">
{{ errorMessage }}
</gl-alert>
<gl-alert v-if="hasPreviousImports" variant="warning" :dismissible="false">
<gl-sprintf
:message="
__(
'You have imported from this project %{numberOfPreviousImportsForProject} times before. Each new import will create duplicate issues.',
)
"
>
<template #numberOfPreviousImportsForProject>{{
numberOfPreviousImportsForProject
}}</template>
</gl-sprintf>
</gl-alert>
<jira-import-setup
v-if="!isJiraConfigured"
......@@ -146,13 +192,15 @@ export default {
<jira-import-progress
v-else-if="isImportInProgress"
:illustration="inProgressIllustration"
:import-initiator="jiraImportDetails.import.scheduledBy.name"
:import-project="jiraImportDetails.import.jiraProjectKey"
:import-time="jiraImportDetails.import.scheduledAt"
:import-initiator="mostRecentImport.scheduledBy.name"
:import-project="mostRecentImport.jiraProjectKey"
:import-time="mostRecentImport.scheduledAt"
:issues-path="issuesPath"
/>
<jira-import-form
v-else
v-model="selectedProject"
:import-label="importLabel"
:issues-path="issuesPath"
:jira-projects="jiraProjectsOptions"
@initiateJiraImport="initiateJiraImport"
......
......@@ -13,6 +13,10 @@ export default {
currentUserAvatarUrl: gon.current_user_avatar_url,
currentUsername: gon.current_username,
props: {
importLabel: {
type: String,
required: true,
},
issuesPath: {
type: String,
required: true,
......@@ -21,21 +25,25 @@ export default {
type: Array,
required: true,
},
value: {
type: String,
required: false,
default: undefined,
},
},
data() {
return {
selectedOption: null,
selectState: null,
};
},
methods: {
initiateJiraImport(event) {
event.preventDefault();
if (!this.selectedOption) {
this.showValidationError();
} else {
if (this.value) {
this.hideValidationError();
this.$emit('initiateJiraImport', this.selectedOption);
this.$emit('initiateJiraImport', this.value);
} else {
this.showValidationError();
}
},
hideValidationError() {
......@@ -62,10 +70,11 @@ export default {
>
<gl-form-select
id="jira-project-select"
v-model="selectedOption"
class="mb-2"
:options="jiraProjects"
:state="selectState"
:value="value"
@change="$emit('input', $event)"
/>
</gl-form-group>
......@@ -79,7 +88,7 @@ export default {
id="jira-project-label"
class="mb-2"
background-color="#428BCA"
title="jira-import::KEY-1"
:title="importLabel"
scoped
/>
</gl-form-group>
......
......@@ -3,7 +3,7 @@
query($fullPath: ID!) {
project(fullPath: $fullPath) {
jiraImportStatus
jiraImports(last: 1) {
jiraImports {
nodes {
...JiraImport
}
......
......@@ -55,7 +55,6 @@ export default class LabelsSelect {
})
.get();
const scopedLabels = $dropdown.data('scopedLabels');
const scopedLabelsDocumentationLink = $dropdown.data('scopedLabelsDocumentationLink');
const { handleClick } = options;
$sidebarLabelTooltip.tooltip();
......@@ -104,7 +103,6 @@ export default class LabelsSelect {
labels: sortBy(data.labels, 'title'),
issueUpdateURL,
enableScopedLabels: scopedLabels,
scopedLabelsDocumentationLink,
});
labelCount = data.labels.length;
......@@ -497,14 +495,6 @@ export default class LabelsSelect {
return escapeStr(label.text_color === '#FFFFFF' ? label.color : label.text_color);
};
const infoIconTemplate = template(
[
'<a href="<%= scopedLabelsDocumentationLink %>" class="gl-link gl-label-icon" target="_blank" rel="noopener">',
'<i class="fa fa-question-circle"></i>',
'</a>',
].join(''),
);
const scopedLabelTemplate = template(
[
'<span class="gl-label gl-label-scoped" style="color: <%= escapeStr(label.color) %>;">',
......@@ -516,7 +506,6 @@ export default class LabelsSelect {
'<%- label.title.slice(label.title.lastIndexOf("::") + 2) %>',
'</span>',
'</a>',
'<%= infoIconTemplate({ label, scopedLabelsDocumentationLink, escapeStr }) %>',
'</span>',
].join(''),
);
......@@ -538,7 +527,7 @@ export default class LabelsSelect {
'<% labels.forEach(function(label){ %>',
'<% if (isScopedLabel(label) && enableScopedLabels) { %>',
'<span class="d-inline-block position-relative scoped-label-wrapper">',
'<%= scopedLabelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, rightLabelTextColor, infoIconTemplate, scopedLabelsDocumentationLink, tooltipTitleTemplate, escapeStr, linkAttrs: \'data-html="true"\' }) %>',
'<%= scopedLabelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, rightLabelTextColor, tooltipTitleTemplate, escapeStr, linkAttrs: \'data-html="true"\' }) %>',
'</span>',
'<% } else { %>',
'<%= labelTemplate({ label, issueUpdateURL, isScopedLabel, enableScopedLabels, tooltipTitleTemplate, escapeStr, linkAttrs: "" }) %>',
......@@ -551,7 +540,6 @@ export default class LabelsSelect {
...tplData,
labelTemplate,
rightLabelTextColor,
infoIconTemplate,
scopedLabelTemplate,
tooltipTitleTemplate,
isScopedLabel,
......
......@@ -240,7 +240,7 @@ export default {
class="d-flex-center text-truncate"
>
<gl-icon name="warning" :size="16" class="flex-shrink-0" />
<span class="text-truncate gl-pl-1">
<span class="text-truncate gl-pl-1-deprecated-no-really-do-not-use-me">
<gl-sprintf
:message="
hasMultipleAlerts ? multipleAlertsSummary.message : singleAlertSummary.message
......
......@@ -268,7 +268,7 @@ export default {
<slot name="topLeft"></slot>
<h5
ref="graphTitle"
class="prometheus-graph-title gl-font-size-large font-weight-bold text-truncate append-right-8"
class="prometheus-graph-title gl-font-lg font-weight-bold text-truncate append-right-8"
>
{{ title }}
</h5>
......
......@@ -10,6 +10,7 @@ import { __ } from '~/locale';
import PipelinesStore from '../../../../pipelines/stores/pipelines_store';
import pipelinesComponent from '../../../../pipelines/components/pipelines.vue';
import Translate from '../../../../vue_shared/translate';
import initVueAlerts from '../../../../vue_alerts';
Vue.use(Translate);
Vue.use(GlToast);
......@@ -55,3 +56,5 @@ document.addEventListener(
},
}),
);
document.addEventListener('DOMContentLoaded', initVueAlerts);
......@@ -12,8 +12,10 @@ import initReadMore from '~/read_more';
import leaveByUrl from '~/namespaces/leave_by_url';
import Star from '../../../star';
import notificationsDropdown from '../../../notifications_dropdown';
import initVueAlerts from '../../../vue_alerts';
document.addEventListener('DOMContentLoaded', () => {
initVueAlerts();
initReadMore();
new Star(); // eslint-disable-line no-new
notificationsDropdown();
......
......@@ -69,7 +69,9 @@ export default {
>
<ci-icon :status="group.status" />
<span class="ci-status-text text-truncate mw-70p gl-pl-1 d-inline-block align-bottom">
<span
class="ci-status-text text-truncate mw-70p gl-pl-1-deprecated-no-really-do-not-use-me d-inline-block align-bottom"
>
{{ group.name }}
</span>
......
......@@ -27,7 +27,9 @@ export default {
<template>
<span class="ci-job-name-component mw-100">
<ci-icon :status="status" />
<span class="ci-status-text text-truncate mw-70p gl-pl-1 d-inline-block align-bottom">
<span
class="ci-status-text text-truncate mw-70p gl-pl-1-deprecated-no-really-do-not-use-me d-inline-block align-bottom"
>
{{ name }}
</span>
</span>
......
......@@ -80,7 +80,9 @@ export default {
<span class="js-short monospace">{{ shortSha(index) }}</span>
</template>
<template slot="expanded">
<span class="js-expanded monospace gl-pl-1">{{ sha(index) }}</span>
<span class="js-expanded monospace gl-pl-1-deprecated-no-really-do-not-use-me">{{
sha(index)
}}</span>
</template>
</expand-button>
<clipboard-button
......
......@@ -41,7 +41,7 @@ export default {
<template>
<div class="card-header d-flex align-items-center bg-white pr-0">
<h2 class="card-title my-2 mr-auto gl-font-size-20">
<h2 class="card-title my-2 mr-auto gl-font-size-20-deprecated-no-really-do-not-use-me">
<gl-link v-if="selfLink" :href="selfLink" class="font-size-inherit">
{{ release.name }}
</gl-link>
......
......@@ -75,7 +75,7 @@ export default {
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
class="js-deploy-meta gl-font-size-12"
class="js-deploy-meta gl-font-sm"
>
{{ deployment.name }}
</gl-link>
......
......@@ -51,7 +51,7 @@ export default {
<div class="mr-widget-extension d-flex align-items-center pl-3">
<div v-if="hasError" class="ci-widget media">
<div class="media-body">
<span class="gl-font-size-small mr-widget-margin-left gl-line-height-24 js-error-state">{{
<span class="gl-font-sm mr-widget-margin-left gl-line-height-24 js-error-state">{{
title
}}</span>
</div>
......
......@@ -169,15 +169,15 @@ export default {
menu-class="date-time-picker-menu"
toggle-class="date-time-picker-toggle text-truncate"
>
<div class="d-flex justify-content-between gl-p-2">
<div class="d-flex justify-content-between gl-p-2-deprecated-no-really-do-not-use-me">
<gl-form-group
v-if="customEnabled"
:label="__('Custom range')"
label-for="custom-from-time"
label-class="gl-pb-1"
class="custom-time-range-form-group col-md-7 gl-pl-1 gl-pr-0 m-0"
label-class="gl-pb-1-deprecated-no-really-do-not-use-me"
class="custom-time-range-form-group col-md-7 gl-pl-1-deprecated-no-really-do-not-use-me gl-pr-0 m-0"
>
<div class="gl-pt-2">
<div class="gl-pt-2-deprecated-no-really-do-not-use-me">
<date-time-picker-input
id="custom-time-from"
v-model="startInput"
......@@ -198,9 +198,12 @@ export default {
</gl-deprecated-button>
</gl-form-group>
</gl-form-group>
<gl-form-group label-for="group-id-dropdown" class="col-md-5 gl-pl-1 gl-pr-1 m-0">
<gl-form-group
label-for="group-id-dropdown"
class="col-md-5 gl-pl-1-deprecated-no-really-do-not-use-me gl-pr-1-deprecated-no-really-do-not-use-me m-0"
>
<template #label>
<span class="gl-pl-5">{{ __('Quick range') }}</span>
<span class="gl-pl-5-deprecated-no-really-do-not-use-me">{{ __('Quick range') }}</span>
</template>
<gl-dropdown-item
......
......@@ -80,11 +80,6 @@ export default {
required: false,
default: false,
},
scopedLabelsDocumentationLink: {
type: String,
required: false,
default: '#',
},
},
computed: {
hiddenInputName() {
......@@ -136,7 +131,6 @@ export default {
<dropdown-value
:labels="context.labels"
:label-filter-base-path="labelFilterBasePath"
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
:enable-scoped-labels="enableScopedLabels"
>
<slot></slot>
......@@ -157,7 +151,6 @@ export default {
:namespace="namespace"
:labels="context.labels"
:show-extra-options="!showCreate"
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
:enable-scoped-labels="enableScopedLabels"
/>
<div
......
......@@ -36,11 +36,6 @@ export default {
required: false,
default: false,
},
scopedLabelsDocumentationLink: {
type: String,
required: false,
default: '#',
},
},
computed: {
dropdownToggleText() {
......@@ -72,7 +67,6 @@ export default {
:data-namespace-path="namespace"
:data-show-any="showExtraOptions"
:data-scoped-labels="enableScopedLabels"
:data-scoped-labels-documentation-link="scopedLabelsDocumentationLink"
type="button"
class="dropdown-menu-toggle wide js-label-select js-multiselect js-context-config-modal"
data-toggle="dropdown"
......
......@@ -20,11 +20,6 @@ export default {
required: false,
default: false,
},
scopedLabelsDocumentationLink: {
type: String,
required: false,
default: '#',
},
},
computed: {
isEmpty() {
......@@ -64,7 +59,6 @@ export default {
:title="label.title"
:description="label.description"
:scoped="showScopedLabels(label)"
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
/>
</template>
</div>
......
......@@ -9,12 +9,7 @@ export default {
GlLabel,
},
computed: {
...mapState([
'selectedLabels',
'allowScopedLabels',
'labelsFilterBasePath',
'scopedLabelsDocumentationPath',
]),
...mapState(['selectedLabels', 'allowScopedLabels', 'labelsFilterBasePath']),
},
methods: {
labelFilterUrl(label) {
......@@ -45,7 +40,6 @@ export default {
:background-color="label.color"
:target="labelFilterUrl(label)"
:scoped="scopedLabel(label)"
:scoped-labels-documentation-link="scopedLabelsDocumentationPath"
tooltip-placement="top"
/>
</template>
......
......@@ -67,11 +67,6 @@ export default {
required: false,
default: '',
},
scopedLabelsDocumentationPath: {
type: String,
required: false,
default: '',
},
labelsListTitle: {
type: String,
required: false,
......@@ -113,7 +108,6 @@ export default {
labelsFetchPath: this.labelsFetchPath,
labelsManagePath: this.labelsManagePath,
labelsFilterBasePath: this.labelsFilterBasePath,
scopedLabelsDocumentationPath: this.scopedLabelsDocumentationPath,
labelsListTitle: this.labelsListTitle,
labelsCreateTitle: this.labelsCreateTitle,
footerCreateLabelTitle: this.footerCreateLabelTitle,
......
......@@ -11,7 +11,6 @@ export default () => ({
namespace: '',
labelsFetchPath: '',
labelsFilterBasePath: '',
scopedLabelsDocumentationPath: '#',
// UI Flags
allowLabelCreate: false,
......
......@@ -37,4 +37,12 @@
@import "application_ee";
// CSS util classes
/**
These are deprecated in favor of the Gitlab UI utilities imported below.
Please check https://unpkg.com/browse/@gitlab/ui/src/scss/utilities.scss
to see the available utility classes.
**/
@import "utilities";
// Gitlab UI util classes
@import "@gitlab/ui/src/scss/utilities";
......@@ -516,11 +516,36 @@ img.emoji {
These will be replaced when the Gitlab UI utilities are included.
**/
@each $index, $padding in $spacing-scale {
#{'.gl-p-#{$index}'} { padding: $padding; }
#{'.gl-pl-#{$index}'} { padding-left: $padding; }
#{'.gl-pr-#{$index}'} { padding-right: $padding; }
#{'.gl-pt-#{$index}'} { padding-top: $padding; }
#{'.gl-pb-#{$index}'} { padding-bottom: $padding; }
#{'.gl-p-#{$index}-deprecated-no-really-do-not-use-me'} { padding: $padding; }
#{'.gl-pl-#{$index}-deprecated-no-really-do-not-use-me'} { padding-left: $padding; }
#{'.gl-pr-#{$index}-deprecated-no-really-do-not-use-me'} { padding-right: $padding; }
#{'.gl-pt-#{$index}-deprecated-no-really-do-not-use-me'} { padding-top: $padding; }
#{'.gl-pb-#{$index}-deprecated-no-really-do-not-use-me'} { padding-bottom: $padding; }
}
/**
The zero-indexed classes will not change and do not need to be updated.
These can be removed when the Gitlab UI class include is merged.
**/
.gl-p-0 {
padding: 0;
}
.gl-pl-0 {
padding-left: 0;
}
.gl-pr-0 {
padding-right: 0;
}
.gl-pt-0 {
padding-top: 0;
}
.gl-pb-0 {
padding-bottom: 0;
}
/**
......@@ -611,15 +636,13 @@ img.emoji {
}
}
.gl-font-size-small { font-size: $gl-font-size-small; }
.gl-font-size-large { font-size: $gl-font-size-large; }
.gl-font-sm { font-size: $gl-font-size-small; }
.gl-font-lg { font-size: $gl-font-size-large; }
.gl-font-base { font-size: $gl-font-size-14; }
.gl-line-height-24 { line-height: $gl-line-height-24; }
.gl-font-size-0 { font-size: 0; }
.gl-font-size-12 { font-size: $gl-font-size-12; }
.gl-font-size-14 { font-size: $gl-font-size-14; }
.gl-font-size-16 { font-size: $gl-font-size-16; }
.gl-font-size-28 { font-size: $gl-font-size-28; }
.gl-font-size-42 { font-size: $gl-font-size-42; }
......
/**
Please note: These are deprecated in favor of the Gitlab UI utility classes.
Check https://unpkg.com/browse/@gitlab/ui/src/scss/utilities.scss
to see the available utility classes. If you cannot find the class you need,
consider adding it to Gitlab UI before adding it here.
**/
@each $variant, $range in $color-ranges {
@each $suffix, $color in $range {
#{'.bg-#{$variant}-#{$suffix}'} {
......@@ -82,6 +89,14 @@
width: px-to-rem(16px);
}
.gl-shim-pb-3 {
padding-bottom: 8px;
}
.gl-shim-pt-5 {
padding-top: 16px;
}
.gl-text-purple { color: $purple; }
.gl-text-gray-800 { color: $gray-800; }
.gl-bg-purple-light { background-color: $purple-light; }
......@@ -124,5 +139,5 @@
.gl-white-space-normal { @include gl-white-space-normal; }
.gl-word-break-all { @include gl-word-break-all; }
.gl-line-height-inherit { line-height: inherit; }
.gl-text-align-inherit { text-align: inherit; }
.gl-reset-line-height { @include gl-reset-line-height; }
.gl-reset-text-align { @include gl-reset-text-align; }
......@@ -8,12 +8,12 @@
- if status.has_details?
= link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item d-flex', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
%span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text.text-truncate.mw-70p.gl-pl-1= subject.name
%span.ci-build-text.text-truncate.mw-70p.gl-pl-1-deprecated-no-really-do-not-use-me= subject.name
- else
.menu-item.mini-pipeline-graph-dropdown-item.d-flex{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } }
%span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text.text-truncate.mw-70p.gl-pl-1= subject.name
%span.ci-build-text.text-truncate.mw-70p.gl-pl-1-deprecated-no-really-do-not-use-me= subject.name
- if status.has_action?
= link_to status.action_path, class: "ci-action-icon-container ci-action-icon-wrapper js-ci-action-icon", method: status.action_method, data: { toggle: 'tooltip', title: status.action_title, container: 'body' } do
......
......@@ -8,7 +8,7 @@
= _("GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security.")
.col-lg-5.order-12
.text-center.mb-3
%h2.font-weight-bold.gl-font-size-20= _('Register for GitLab')
%h2.font-weight-bold.gl-font-size-20-deprecated-no-really-do-not-use-me= _('Register for GitLab')
= render 'devise/shared/experimental_separate_sign_up_flow_box'
= render 'devise/shared/sign_in_link'
- else
......
.template-selectors-menu.gl-pl-2
.template-selectors-menu.gl-pl-2-deprecated-no-really-do-not-use-me
.template-selector-dropdowns-wrap
.template-type-selector.js-template-type-selector-wrap.hidden
- toggle_text = should_suggest_gitlab_ci_yml? ? '.gitlab-ci.yml' : 'Select a template type'
......
- page_title _('Contributors')
.sub-header-block.bg-gray-light.gl-p-3
.sub-header-block.bg-gray-light.gl-p-3-deprecated-no-really-do-not-use-me
.tree-ref-holder.inline.vertical-align-middle
= render 'shared/ref_switcher', destination: 'graphs'
= link_to s_('Commits|History'), project_commits_path(@project, current_ref), class: 'btn'
......
......@@ -38,8 +38,7 @@
":description" => "list.label.description",
"tooltipPlacement" => "bottom",
":size" => '(!list.isExpanded ? "sm" : "")',
":scoped" => "showScopedLabels(list.label)",
":scoped-labels-documentation-link" => "helpLink" }
":scoped" => "showScopedLabels(list.label)" }
- if can?(current_user, :admin_list, current_board_parent)
%board-delete{ "inline-template" => true,
......
......@@ -12,8 +12,7 @@
":background-color" => "label.color",
":title" => "label.title",
":description" => "label.description",
":scoped" => "showScopedLabels(label)",
":scoped-labels-documentation-link" => "helpLink" }
":scoped" => "showScopedLabels(label)" }
- if can_admin_issue?
.selectbox
......
......@@ -6,7 +6,7 @@
- source_title, target_title = format_mr_branch_names(@merge_request)
.form-group.row.d-flex.gl-pl-3.gl-pr-3.branch-selector
.form-group.row.d-flex.gl-pl-3-deprecated-no-really-do-not-use-me.gl-pr-3-deprecated-no-really-do-not-use-me.branch-selector
.align-self-center
%span
= _('From <code>%{source_title}</code> into').html_safe % { source_title: source_title }
......
......@@ -9,7 +9,7 @@ class ProjectExportWorker # rubocop:disable Scalability/IdempotentWorker
worker_resource_boundary :memory
urgency :throttled
def perform(current_user_id, project_id, after_export_strategy = {}, params = {})
def perform(current_user_id, project_id, after_export_strategy = {}, params = {}, options = {})
current_user = User.find(current_user_id)
project = Project.find(project_id)
export_job = project.export_jobs.safe_find_or_create_by(jid: self.jid)
......@@ -17,7 +17,7 @@ class ProjectExportWorker # rubocop:disable Scalability/IdempotentWorker
export_job&.start
::Projects::ImportExport::ExportService.new(project, current_user, params).execute(after_export)
::Projects::ImportExport::ExportService.new(project, current_user, params).execute(after_export, options)
export_job&.finish
rescue ActiveRecord::RecordNotFound, Gitlab::ImportExport::AfterExportStrategyBuilder::StrategyNotFoundError => e
......
---
title: Fix discard button not showing for new empty files in Web IDE
merge_request: 30767
author:
type: fixed
---
title: Show correct label and count on Jira import form
merge_request: 30072
author:
type: changed
---
title: Use Jira import owner as the issue author when importing issues from Jira
merge_request: 30504
author:
type: fixed
# frozen_string_literal: true
common = 'app/assets/stylesheets/framework/common.scss'
utilities = 'app/assets/stylesheets/utilities.scss'
def get_css_files(files, common_filepath, utilities_filepath)
files.select do |file|
file.include?(common_filepath) ||
file.include?(utilities_filepath)
end
end
changed_util_files = get_css_files(helper.all_changed_files.to_a, common, utilities)
unless changed_util_files.empty?
markdown(<<~MARKDOWN)
## Changes to utility SCSS files
MARKDOWN
if changed_util_files.include?(common)
markdown(<<~MARKDOWN)
### Addition to `#{common}`
You have added a new rule to `#{common}`. Are you sure you need this rule?
If it is a component class shared across items, could it be added to the component as a utility class or to the component's stylesheet? If not, you can ignore this warning.
If it is a new utility class, is there another class that shares the same values in either this file or in `#{utilities}`? If not, please add it to `#{utilities}`, following the [Gitlab UI naming style](https://unpkg.com/browse/@gitlab/ui/src/scss/utilities.scss).
MARKDOWN
end
if changed_util_files.include?(utilities)
markdown(<<~MARKDOWN)
### Addition to `#{utilities}`
You have added a new rule to `#{utilities}`. Are you sure you need this rule?
If it is a component class shared across items, could it be added to the component as a utility class or to the component's stylesheet? If not, consider adding it to `#{common}`
If it is a new utility class, is there another class that shares the same values in either this file or in `#{utilities}`? If not, please be sure this addition follows the [Gitlab UI naming style](https://unpkg.com/browse/@gitlab/ui/src/scss/utilities.scss) so it may be removed when these rules are included. See [Include gitlab-ui utility-class library](https://gitlab.com/gitlab-org/gitlab/issues/36857) for more about this project.
MARKDOWN
end
warn "This merge request adds a new rule to #{common} or #{utilities}."
end
......@@ -20,7 +20,7 @@ Response Code Legend:
## Configuration
Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service url.
Set the `EXTERNAL_VALIDATION_SERVICE_URL` to the external service URL.
## Payload Schema
......
......@@ -79,8 +79,8 @@ is recommended over [NFS](nfs.md) wherever possible for improved performance.
NOTE: **Note:** To maintain uniformity of links across HA clusters, the `external_url`
on the first application server as well as the additional application
servers should point to the external url that users will use to access GitLab.
In a typical HA setup, this will be the url of the load balancer which will
servers should point to the external URL that users will use to access GitLab.
In a typical HA setup, this will be the URL of the load balancer which will
route traffic to all GitLab application servers in the HA cluster.
NOTE: **Note:** When you specify `https` in the `external_url`, as in the example
......
......@@ -129,7 +129,7 @@ Here is a configuration example with Rackspace Cloud Files.
NOTE: **Note:**
Regardless of whether the container has public access enabled or disabled, Fog will
use the TempURL method to grant access to LFS objects. If you see errors in logs referencing
instantiating storage with a temp-url-key, ensure that you have set the key properly
instantiating storage with a `temp-url-key`, ensure that you have set the key properly
on the Rackspace API and in `gitlab.rb`. You can verify the value of the key Rackspace
has set by sending a GET request with token header to the service access endpoint URL
and comparing the output of the returned headers.
......
......@@ -338,7 +338,7 @@ POST /projects/:id/releases
| `milestones` | array of string | no | The title of each milestone the release is associated with. |
| `assets:links` | array of hash | no | An array of assets links. |
| `assets:links:name`| string | required by: `assets:links` | The name of the link. |
| `assets:links:url` | string | required by: `assets:links` | The url of the link. |
| `assets:links:url` | string | required by: `assets:links` | The URL of the link. |
| `assets:links:filepath` | string | no | Optional path for a [Direct Asset link](../../user/project/releases.md).
| `released_at` | datetime | no | The date when the release will be/was ready. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
......
......@@ -47,7 +47,7 @@ Example response:
Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit
NOTE: **Note:**
......@@ -122,7 +122,7 @@ Example response:
Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit
NOTE: **Note:**
......@@ -161,7 +161,7 @@ curl --header 'PRIVATE-TOKEN: <your_access_token>' 'https://gitlab.example.com/a
Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit
NOTE: **Note:**
......@@ -193,7 +193,7 @@ Example response:
Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `branch` (required) - Name of the branch
- `start_branch` (optional) - Name of the branch to start the new commit from
- `encoding` (optional) - Change encoding to 'base64'. Default is text.
......@@ -228,7 +228,7 @@ Example response:
Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `branch` (required) - Name of the branch
- `start_branch` (optional) - Name of the branch to start the new commit from
- `encoding` (optional) - Change encoding to 'base64'. Default is text.
......@@ -265,7 +265,7 @@ curl --request DELETE --header 'PRIVATE-TOKEN: <your_access_token>' --header "Co
Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `file_path` (required) - URL encoded full path to new file. Ex. lib%2Fclass%2Erb
- `branch` (required) - Name of the branch
- `start_branch` (optional) - Name of the branch to start the new commit from
- `author_email` (optional) - Specify the commit author's email address
......
......@@ -189,9 +189,9 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `new_issue_url` | string | true | New Issue url |
| `issues_url` | string | true | Issue url |
| `project_url` | string | true | Project url |
| `new_issue_url` | string | true | New Issue URL |
| `issues_url` | string | true | Issue URL |
| `project_url` | string | true | Project URL |
| `description` | string | false | Description |
| `title` | string | false | Title |
| `push_events` | boolean | false | Enable notifications for push events |
......@@ -347,11 +347,11 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `new_issue_url` | string | true | New Issue url
| `issues_url` | string | true | Issue url
| `project_url` | string | true | Project url
| `description` | string | false | Description
| `title` | string | false | Title
| `new_issue_url` | string | true | New Issue URL |
| `issues_url` | string | true | Issue URL |
| `project_url` | string | true | Project URL |
| `description` | string | false | Description |
| `title` | string | false | Title |
| `push_events` | boolean | false | Enable notifications for push events |
### Delete Custom Issue Tracker service
......@@ -1034,9 +1034,9 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `new_issue_url` | string | true | New Issue url |
| `project_url` | string | true | Project url |
| `issues_url` | string | true | Issue url |
| `new_issue_url` | string | true | New Issue URL |
| `project_url` | string | true | Project URL |
| `issues_url` | string | true | Issue URL |
| `description` | string | false | Description |
| `push_events` | boolean | false | Enable notifications for push events |
......@@ -1385,8 +1385,8 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `issues_url` | string | true | Issue url |
| `project_url` | string | true | Project url |
| `issues_url` | string | true | Issue URL |
| `project_url` | string | true | Project URL |
| `description` | string | false | Description |
| `push_events` | boolean | false | Enable notifications for push events |
......
......@@ -240,7 +240,7 @@ are listed in the descriptions of the relevant settings.
| `elasticsearch_namespace_ids` | array of integers | no | **(PREMIUM)** The namespaces to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
| `elasticsearch_project_ids` | array of integers | no | **(PREMIUM)** The projects to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
| `elasticsearch_search` | boolean | no | **(PREMIUM)** Enable Elasticsearch search |
| `elasticsearch_url` | string | no | **(PREMIUM)** The url to use for connecting to Elasticsearch. Use a comma-separated list to support cluster (for example, `http://localhost:9200, http://localhost:9201"`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (for example, `http://<username>:<password>@<elastic_host>:9200/`). |
| `elasticsearch_url` | string | no | **(PREMIUM)** The URL to use for connecting to Elasticsearch. Use a comma-separated list to support cluster (for example, `http://localhost:9200, http://localhost:9201"`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (for example, `http://<username>:<password>@<elastic_host>:9200/`). |
| `email_additional_text` | string | no | **(PREMIUM)** Additional text added to the bottom of every email for legal/auditing/compliance reasons |
| `email_author_in_body` | boolean | no | Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead. |
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
......
......@@ -374,13 +374,13 @@ Once you've picked a new Go version to use, the steps to update Omnibus and CNG
are:
- [Create a merge request in the CNG project](https://gitlab.com/gitlab-org/build/CNG/edit/master/ci_files/variables.yml?branch_name=update-go-version),
updating the `GO_VERSION` in `ci_files/variables.yml`.
updating the `GO_VERSION` in `ci_files/variables.yml`.
- Create a merge request in the [`gitlab-omnibus-builder` project](https://gitlab.com/gitlab-org/gitlab-omnibus-builder),
updating every file in the `docker/` directory so the `GO_VERSION` is set
appropriately. [Here's an example](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/-/merge_requests/125/diffs).
updating every file in the `docker/` directory so the `GO_VERSION` is set
appropriately. [Here's an example](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/-/merge_requests/125/diffs).
- Tag a new release of `gitlab-omnibus-builder` containing the change.
- [Create a merge request in the `gitlab-omnibus` project](https://gitlab.com/gitlab-org/omnibus-gitlab/edit/master/.gitlab-ci.yml?branch_name=update-gitlab-omnibus-builder-version),
updating the `BUILDER_IMAGE_REVISION` to match the newly-created tag.
- [Create a merge request in the `omnibus-gitlab` project](https://gitlab.com/gitlab-org/omnibus-gitlab/edit/master/.gitlab-ci.yml?branch_name=update-gitlab-omnibus-builder-version),
updating the `BUILDER_IMAGE_REVISION` to match the newly-created tag.
To reduce unnecessary differences between two distribution methods, Omnibus and
CNG **should always use the same Go version**.
......
......@@ -110,7 +110,7 @@ The way new package systems are integrated in GitLab is using an [MVC](https://a
Required actions are all the additional requests that GitLab will need to handle so the corresponding package manager CLI can work properly. It could be a search feature or an endpoint providing meta information about a package. For example:
- For NuGet, the search request was implemented during the first MVC iteration, to support Visual Studio.
- For NPM, there is a metadata endpoint used by `npm` to get the tarball url.
- For NPM, there is a metadata endpoint used by `npm` to get the tarball URL.
For the first MVC iteration, it's recommended to stay at the project level of the [remote hierarchy](#remote-hierarchy). Other levels can be tackled with [future Merge Requests](#future-work).
......
......@@ -638,7 +638,7 @@ Karma allows something similar, but it's way more costly.
Running Karma with `yarn run karma-start` will compile the JavaScript
assets and run a server at `http://localhost:9876/` where it will automatically
run the tests on any browser which connects to it. You can enter that url on
run the tests on any browser which connects to it. You can enter that URL on
multiple browsers at once to have it run the tests on each in parallel.
While Karma is running, any changes you make will instantly trigger a recompile
......
......@@ -209,6 +209,31 @@ Leading indicators may be health check failures leading to restarts or majority
The [Review Apps Overview dashboard](https://app.google.stackdriver.com/dashboards/6798952013815386466?project=gitlab-review-apps&timeDomain=1d)
aids in identifying load spikes on the cluster, and if nodes are problematic or the entire cluster is trending towards unhealthy.
### Release failed with `ImagePullBackOff`
**Potential cause:**
If you see an `ImagePullBackoff` status, check for a missing Docker image.
**Where to look for further debugging:**
To check that the Docker images were created, run the following Docker command:
```shell
`DOCKER_CLI_EXPERIMENTAL=enabled docker manifest repository:tag`
```
The output of this command indicates if the Docker image exists. For example:
```shell
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect registry.gitlab.com/gitlab-org/build/cng-mirror/gitlab-rails-ee:39467-allow-a-release-s-associated-milestones-to-be-edited-thro
```
If the Docker image does not exist:
- Verify the `image.repository` and `image.tag` options in the `helm upgrade --install` command match the repository names used by CNG-mirror pipeline.
- Look further in the corresponding downstream CNG-mirror pipeline in `review-build-cng` job.
### Node count is always increasing (i.e. never stabilizing or decreasing)
**Potential cause:**
......
......@@ -164,7 +164,7 @@ Click the links to see your GitLab repository data.
## Limitations
- This integration is currently not supported on GitLab instances under a [relative url](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab) (for example, `http://example.com/gitlab`).
- This integration is currently not supported on GitLab instances under a [relative URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab) (for example, `http://example.com/gitlab`).
## Changelog
......
......@@ -220,7 +220,7 @@ instance is being served on HTTP.
This behavior is caused by Git LFS using HTTPS connections by default when a
`lfsurl` is not set in the Git config.
To prevent this from happening, set the lfs url in project Git config:
To prevent this from happening, set the lfs URL in project Git config:
```shell
git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs"
......@@ -265,7 +265,7 @@ If you are storing LFS files outside of GitLab you can disable LFS on the projec
### Hosting LFS objects externally
It is possible to host LFS objects externally by setting a custom LFS url with `git config -f .lfsconfig lfs.url https://example.com/<project>.git/info/lfs`.
It is possible to host LFS objects externally by setting a custom LFS URL with `git config -f .lfsconfig lfs.url https://example.com/<project>.git/info/lfs`.
You might choose to do this if you are using an appliance like a Sonatype Nexus to store LFS data. If you choose to use an external LFS store,
GitLab will not be able to verify LFS objects which means that pushes will fail if you have GitLab LFS support enabled.
......
......@@ -103,7 +103,7 @@ Access the default page for admin area settings by navigating to
| Option | Description |
| ------ | ----------- |
| [Email](email.md) | Various email settings. |
| [Help page](../../../customization/help_message.md) | Help page text and support page url. |
| [Help page](../../../customization/help_message.md) | Help page text and support page URL. |
| [Pages](../../../administration/pages/index.md#custom-domain-verification) | Size and domain settings for static websites |
| [Real-time features](../../../administration/polling.md) | Change this value to influence how frequently the GitLab UI polls for updates. |
| [Gitaly timeouts](gitaly_timeouts.md) | Configure Gitaly timeouts. |
......
......@@ -38,7 +38,7 @@ In [GitLab Premium or higher](https://about.gitlab.com/pricing/), GitLab adminis
To do this:
1. Uncheck the **Allow owners to manage default branch protection in groups** checkbox.
1. Uncheck the **Allow owners to manage default branch protection per group** checkbox.
NOTE: **Note:**
GitLab administrators can still update the default branch protection of a group.
......
......@@ -150,7 +150,7 @@ endpoints:
Running the following `curl` command should trigger your function.
NOTE: **Note:**
Your url should be the one retrieved from the GitLab deploy stage log.
Your URL should be the one retrieved from the GitLab deploy stage log.
```shell
curl https://u768nzby1j.execute-api.us-east-1.amazonaws.com/production/hello
......
......@@ -93,7 +93,7 @@ Integration with Prometheus requires the following:
#### Getting started
Installing and configuring Prometheus to monitor applications is fairly straight forward.
Installing and configuring Prometheus to monitor applications is fairly straightforward.
1. [Install Prometheus](https://prometheus.io/docs/prometheus/latest/installation/)
1. Set up one of the [supported monitoring targets](prometheus_library/index.md)
......@@ -146,7 +146,7 @@ one of them will be used:
Once configured, GitLab will attempt to retrieve performance metrics for any
environment which has had a successful deployment.
GitLab will automatically scan the Prometheus server for metrics from known servers like Kubernetes and NGINX, and attempt to identify individual environment. The supported metrics and scan process is detailed in our [Prometheus Metrics Library documentation](prometheus_library/index.md).
GitLab will automatically scan the Prometheus server for metrics from known servers like Kubernetes and NGINX, and attempt to identify individual environments. The supported metrics and scan process is detailed in our [Prometheus Metrics Library documentation](prometheus_library/index.md).
You can view the performance dashboard for an environment by [clicking on the monitoring button](../../../ci/environments.md#monitoring-environments).
......@@ -170,7 +170,7 @@ A few fields are required:
- **Y-axis label**: Y axis title to display on the dashboard.
- **Unit label**: Query units, for example `req / sec`. Shown next to the value.
Multiple metrics can be displayed on the same chart if the fields **Name**, **Type**, and **Y-axis label** match between metrics. For example, a metric with **Name** `Requests Rate`, **Type** `Business`, and **Y-axis label** `rec / sec` would display on the same chart as a second metric with the same values. A **Legend label** is suggested if this feature used.
Multiple metrics can be displayed on the same chart if the fields **Name**, **Type**, and **Y-axis label** match between metrics. For example, a metric with **Name** `Requests Rate`, **Type** `Business`, and **Y-axis label** `rec / sec` would display on the same chart as a second metric with the same values. A **Legend label** is suggested if this feature is used.
#### Query Variables
......@@ -279,7 +279,7 @@ If you select another branch, this branch should be merged to your **default** b
Dashboards have several components:
- Panel groups, which comprise of panels.
- Panel groups, which consist of panels.
- Panels, which support one or more metrics.
The following tables outline the details of expected properties.
......@@ -633,7 +633,7 @@ Note the following properties:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/34779) in GitLab 12.5.
When viewing a custom dashboard of a project, you can view the original
`.yml` file by clicking on **Edit dashboard** button.
`.yml` file by clicking on the **Edit dashboard** button.
### Chart Context Menu
......@@ -733,7 +733,7 @@ In order for GitLab to associate your alerts with an [environment](../../../ci/e
>- [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/4925) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.11.
>- [From GitLab Ultimate 12.5](https://gitlab.com/gitlab-org/gitlab/issues/13401), when GitLab receives a recovery alert, it will automatically close the associated issue.
Alerts can be used to trigger actions, like open an issue automatically (enabled by default since `12.1`). To configure the actions:
Alerts can be used to trigger actions, like opening an issue automatically (enabled by default since `12.1`). To configure the actions:
1. Navigate to your project's **Settings > Operations > Incidents**.
1. Enable the option to create issues.
......@@ -752,7 +752,7 @@ Once enabled, an issue will be opened automatically when an alert is triggered w
- Optional list of attached annotations extracted from `annotations/*`
- Alert [GFM](../../markdown.md): GitLab Flavored Markdown from `annotations/gitlab_incident_markdown`
When GitLab receives a **Recovery Alert**, it will automatically close the associated issue. This action will be recorded as a system message on the issue indicated that it was closed automatically by the GitLab Alert bot.
When GitLab receives a **Recovery Alert**, it will automatically close the associated issue. This action will be recorded as a system message on the issue indicating that it was closed automatically by the GitLab Alert bot.
To further customize the issue, you can add labels, mentions, or any other supported [quick action](../quick_actions.md) in the selected issue template, which will apply to all incidents. To limit quick actions or other information to only specific types of alerts, use the `annotations/gitlab_incident_markdown` field.
......@@ -925,7 +925,7 @@ Prerequisites for embedding from a Grafana instance:
1. In the upper-left corner of the page, select a specific value for each variable required for the queries in the chart.
![Select Query Variables](img/select_query_variables_v12_5.png)
1. In Grafana, click on a panel's title, then click **Share** to open the panel's sharing dialog to the **Link** tab. If you click the _dashboard's_ share panel instead, GitLab will attempt to embed the first supported panel on the dashboard (if available).
1. If your Prometheus queries use Grafana's custom template variables, ensure that "Template variables" option is toggled to **On**. Of Grafana global template variables, only `$__interval`, `$__from`, and `$__to` are currently supported. Toggle **On** the "Current time range" option to specify the time range of the chart. Otherwise, the default range will be the last 8 hours.
1. If your Prometheus queries use Grafana's custom template variables, ensure that the "Template variables" option is toggled to **On**. Of Grafana global template variables, only `$__interval`, `$__from`, and `$__to` are currently supported. Toggle **On** the "Current time range" option to specify the time range of the chart. Otherwise, the default range will be the last 8 hours.
![Grafana Sharing Dialog](img/grafana_sharing_dialog_v12_5.png)
1. Click **Copy** to copy the URL to the clipboard.
1. In GitLab, paste the URL into a Markdown field and save. The chart will take a few moments to render.
......
......@@ -3,12 +3,13 @@
module Gitlab
module JiraImport
class BaseImporter
attr_reader :project, :client, :formatter, :jira_project_key
attr_reader :project, :client, :formatter, :jira_project_key, :running_import
def initialize(project)
project.validate_jira_import_settings!
@jira_project_key = project.latest_jira_import&.jira_project_key
@running_import = project.latest_jira_import
@jira_project_key = running_import&.jira_project_key
raise Projects::ImportService::Error, _('Unable to find Jira project to import data from.') unless @jira_project_key
......
......@@ -3,11 +3,12 @@
module Gitlab
module JiraImport
class IssueSerializer
attr_reader :jira_issue, :project, :params, :formatter
attr_reader :jira_issue, :project, :import_owner_id, :params, :formatter
def initialize(project, jira_issue, params = {})
def initialize(project, jira_issue, import_owner_id, params = {})
@jira_issue = jira_issue
@project = project
@import_owner_id = import_owner_id
@params = params
@formatter = Gitlab::ImportFormatter.new
end
......@@ -71,7 +72,7 @@ module Gitlab
end
def reporter
map_user_id(jira_issue&.reporter&.emailAddress) || project.creator_id
map_user_id(jira_issue&.reporter&.emailAddress) || import_owner_id
end
def assignees
......
......@@ -57,7 +57,7 @@ module Gitlab
# For such cases we exit early if issue was already imported.
next if already_imported?(jira_issue.id)
issue_attrs = IssueSerializer.new(project, jira_issue, { iid: next_iid }).execute
issue_attrs = IssueSerializer.new(project, jira_issue, running_import.user_id, { iid: next_iid }).execute
Gitlab::JiraImport::ImportIssueWorker.perform_async(project.id, jira_issue.id, issue_attrs, job_waiter.key)
job_waiter.jobs_remaining += 1
......
......@@ -12,6 +12,7 @@ class GitlabDanger
database
commit_messages
telemetry
utility_css
].freeze
CI_ONLY_RULES ||= %w[
......
......@@ -3357,6 +3357,9 @@ msgstr ""
msgid "Buy GitLab Enterprise Edition"
msgstr ""
msgid "Buy more Pipeline minutes"
msgstr ""
msgid "By %{user_name}"
msgstr ""
......@@ -4152,9 +4155,6 @@ msgstr ""
msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
msgstr ""
msgid "Click here"
msgstr ""
msgid "Click the <strong>Download</strong> button and wait for downloading to complete."
msgstr ""
......@@ -14923,15 +14923,6 @@ msgstr ""
msgid "Pipelines settings for '%{project_name}' were successfully updated."
msgstr ""
msgid "Pipelines| to purchase more minutes."
msgstr ""
msgid "Pipelines|%{namespace_name} has exceeded its pipeline minutes quota."
msgstr ""
msgid "Pipelines|%{namespace_name} has less than %{notification_level}%% of CI minutes available."
msgstr ""
msgid "Pipelines|API"
msgstr ""
......@@ -14953,10 +14944,13 @@ msgstr ""
msgid "Pipelines|Get started with Pipelines"
msgstr ""
msgid "Pipelines|Loading Pipelines"
msgid "Pipelines|Group %{namespace_name} has %{percentage}%% or less Shared Runner Pipeline minutes remaining. Once it runs out, no new jobs or pipelines in its projects will run."
msgstr ""
msgid "Pipelines|Group %{namespace_name} has exceeded its pipeline minutes quota. Unless you buy additional pipeline minutes, no new jobs or pipelines in its projects will run."
msgstr ""
msgid "Pipelines|Pipelines will not run anymore on shared Runners."
msgid "Pipelines|Loading Pipelines"
msgstr ""
msgid "Pipelines|Project cache successfully reset."
......@@ -24096,6 +24090,9 @@ msgstr ""
msgid "You have declined the invitation to join %{label}."
msgstr ""
msgid "You have imported from this project %{numberOfPreviousImportsForProject} times before. Each new import will create duplicate issues."
msgstr ""
msgid "You have no permissions"
msgstr ""
......
......@@ -7,27 +7,32 @@ import { file } from '../../helpers';
const localVue = createLocalVue();
localVue.use(Vuex);
const TEST_FILE_PATH = 'test/file/path';
describe('IDE commit editor header', () => {
let wrapper;
let f;
let store;
const findDiscardModal = () => wrapper.find({ ref: 'discardModal' });
const findDiscardButton = () => wrapper.find({ ref: 'discardButton' });
beforeEach(() => {
f = file('file');
store = createStore();
const createComponent = (fileProps = {}) => {
wrapper = mount(EditorHeader, {
store,
localVue,
propsData: {
activeFile: f,
activeFile: {
...file(TEST_FILE_PATH),
staged: true,
...fileProps,
},
},
});
};
jest.spyOn(wrapper.vm, 'discardChanges').mockImplementation();
const findDiscardModal = () => wrapper.find({ ref: 'discardModal' });
const findDiscardButton = () => wrapper.find({ ref: 'discardButton' });
beforeEach(() => {
store = createStore();
jest.spyOn(store, 'dispatch').mockImplementation();
});
afterEach(() => {
......@@ -35,29 +40,38 @@ describe('IDE commit editor header', () => {
wrapper = null;
});
it('renders button to discard', () => {
expect(wrapper.vm.$el.querySelectorAll('.btn')).toHaveLength(1);
it.each`
fileProps | shouldExist
${{ staged: false, changed: false }} | ${false}
${{ staged: true, changed: false }} | ${true}
${{ staged: false, changed: true }} | ${true}
${{ staged: true, changed: true }} | ${true}
`('with $fileProps, show discard button is $shouldExist', ({ fileProps, shouldExist }) => {
createComponent(fileProps);
expect(findDiscardButton().exists()).toBe(shouldExist);
});
describe('discard button', () => {
let modal;
beforeEach(() => {
modal = findDiscardModal();
createComponent();
const modal = findDiscardModal();
jest.spyOn(modal.vm, 'show');
findDiscardButton().trigger('click');
});
it('opens a dialog confirming discard', () => {
expect(modal.vm.show).toHaveBeenCalled();
expect(findDiscardModal().vm.show).toHaveBeenCalled();
});
it('calls discardFileChanges if dialog result is confirmed', () => {
modal.vm.$emit('ok');
expect(store.dispatch).not.toHaveBeenCalled();
findDiscardModal().vm.$emit('ok');
expect(wrapper.vm.discardChanges).toHaveBeenCalledWith(f.path);
expect(store.dispatch).toHaveBeenCalledWith('discardFileChanges', TEST_FILE_PATH);
});
});
});
import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { mount, shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import JiraImportApp from '~/jira_import/components/jira_import_app.vue';
import JiraImportForm from '~/jira_import/components/jira_import_form.vue';
......@@ -11,12 +11,16 @@ import { IMPORT_STATE } from '~/jira_import/utils';
const mountComponent = ({
isJiraConfigured = true,
errorMessage = '',
showAlert = true,
selectedProject = 'MTG',
showAlert = false,
status = IMPORT_STATE.NONE,
loading = false,
mutate = jest.fn(() => Promise.resolve()),
} = {}) =>
shallowMount(JiraImportApp, {
mountType,
} = {}) => {
const mountFunction = mountType === 'mount' ? mount : shallowMount;
return mountFunction(JiraImportApp, {
propsData: {
isJiraConfigured,
inProgressIllustration: 'in-progress-illustration.svg',
......@@ -34,15 +38,32 @@ const mountComponent = ({
return {
errorMessage,
showAlert,
selectedProject,
jiraImportDetails: {
status,
import: {
jiraProjectKey: 'MTG',
scheduledAt: '2020-04-08T12:17:25+00:00',
scheduledBy: {
name: 'Jane Doe',
imports: [
{
jiraProjectKey: 'MTG',
scheduledAt: '2020-04-08T10:11:12+00:00',
scheduledBy: {
name: 'John Doe',
},
},
},
{
jiraProjectKey: 'MSJP',
scheduledAt: '2020-04-09T13:14:15+00:00',
scheduledBy: {
name: 'Jimmy Doe',
},
},
{
jiraProjectKey: 'MTG',
scheduledAt: '2020-04-09T16:17:18+00:00',
scheduledBy: {
name: 'Jane Doe',
},
},
],
},
};
},
......@@ -53,6 +74,7 @@ const mountComponent = ({
},
},
});
};
describe('JiraImportApp', () => {
let wrapper;
......@@ -160,6 +182,64 @@ describe('JiraImportApp', () => {
});
});
describe('import in progress screen', () => {
beforeEach(() => {
wrapper = mountComponent({ status: IMPORT_STATE.SCHEDULED });
});
it('shows the illustration', () => {
expect(getProgressComponent().props('illustration')).toBe('in-progress-illustration.svg');
});
it('shows the name of the most recent import initiator', () => {
expect(getProgressComponent().props('importInitiator')).toBe('Jane Doe');
});
it('shows the name of the most recent imported project', () => {
expect(getProgressComponent().props('importProject')).toBe('MTG');
});
it('shows the time of the most recent import', () => {
expect(getProgressComponent().props('importTime')).toBe('2020-04-09T16:17:18+00:00');
});
it('has the path to the issues page', () => {
expect(getProgressComponent().props('issuesPath')).toBe('gitlab-org/gitlab-test/-/issues');
});
});
describe('jira import form screen', () => {
describe('when selected project has been imported before', () => {
it('shows jira-import::MTG-3 label since project MTG has been imported 2 time before', () => {
wrapper = mountComponent();
expect(getFormComponent().props('importLabel')).toBe('jira-import::MTG-3');
});
it('shows warning alert to explain project MTG has been imported 2 times before', () => {
wrapper = mountComponent({ mountType: 'mount' });
expect(getAlert().text()).toBe(
'You have imported from this project 2 times before. Each new import will create duplicate issues.',
);
});
});
describe('when selected project has not been imported before', () => {
beforeEach(() => {
wrapper = mountComponent({ selectedProject: 'MJP' });
});
it('shows jira-import::MJP-1 label since project MJP has not been imported before', () => {
expect(getFormComponent().props('importLabel')).toBe('jira-import::MJP-1');
});
it('does not show warning alert since project MJP has not been imported before', () => {
expect(getAlert().exists()).toBe(false);
});
});
});
describe('initiating a Jira import', () => {
it('calls the mutation with the expected arguments', () => {
const mutate = jest.fn(() => Promise.resolve());
......@@ -201,6 +281,7 @@ describe('JiraImportApp', () => {
wrapper = mountComponent({
errorMessage: 'There was an error importing the Jira project.',
showAlert: true,
selectedProject: null,
});
expect(getAlert().exists()).toBe(true);
......
......@@ -2,11 +2,15 @@ import { GlAvatar, GlButton, GlFormSelect, GlLabel } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import JiraImportForm from '~/jira_import/components/jira_import_form.vue';
const importLabel = 'jira-import::MTG-1';
const value = 'MTG';
const mountComponent = ({ mountType } = {}) => {
const mountFunction = mountType === 'mount' ? mount : shallowMount;
return mountFunction(JiraImportForm, {
propsData: {
importLabel,
issuesPath: 'gitlab-org/gitlab-test/-/issues',
jiraProjects: [
{
......@@ -22,6 +26,7 @@ const mountComponent = ({ mountType } = {}) => {
value: 'MTG',
},
],
value,
},
});
};
......@@ -29,6 +34,8 @@ const mountComponent = ({ mountType } = {}) => {
describe('JiraImportForm', () => {
let wrapper;
const getSelectDropdown = () => wrapper.find(GlFormSelect);
const getCancelButton = () => wrapper.findAll(GlButton).at(1);
afterEach(() => {
......@@ -40,7 +47,7 @@ describe('JiraImportForm', () => {
it('is shown', () => {
wrapper = mountComponent();
expect(wrapper.find(GlFormSelect).exists()).toBe(true);
expect(wrapper.contains(GlFormSelect)).toBe(true);
});
it('contains a list of Jira projects to select from', () => {
......@@ -48,8 +55,7 @@ describe('JiraImportForm', () => {
const optionItems = ['My Jira Project', 'My Second Jira Project', 'Migrate to GitLab'];
wrapper
.find(GlFormSelect)
getSelectDropdown()
.findAll('option')
.wrappers.forEach((optionEl, index) => {
expect(optionEl.text()).toBe(optionItems[index]);
......@@ -63,7 +69,7 @@ describe('JiraImportForm', () => {
});
it('shows a label which will be applied to imported Jira projects', () => {
expect(wrapper.find(GlLabel).attributes('title')).toBe('jira-import::KEY-1');
expect(wrapper.find(GlLabel).props('title')).toBe(importLabel);
});
it('shows information to the user', () => {
......@@ -77,7 +83,7 @@ describe('JiraImportForm', () => {
});
it('shows an avatar for the Reporter', () => {
expect(wrapper.find(GlAvatar).exists()).toBe(true);
expect(wrapper.contains(GlAvatar)).toBe(true);
});
it('shows jira.issue.description.content for the Description', () => {
......@@ -111,16 +117,19 @@ describe('JiraImportForm', () => {
});
});
it('emits an "initiateJiraImport" event with the selected dropdown value when submitted', () => {
const selectedOption = 'MTG';
it('emits an "input" event when the input select value changes', () => {
wrapper = mountComponent({ mountType: 'mount' });
getSelectDropdown().vm.$emit('change', value);
expect(wrapper.emitted('input')[0]).toEqual([value]);
});
it('emits an "initiateJiraImport" event with the selected dropdown value when submitted', () => {
wrapper = mountComponent();
wrapper.setData({
selectedOption,
});
wrapper.find('form').trigger('submit');
expect(wrapper.emitted('initiateJiraImport')[0]).toEqual([selectedOption]);
expect(wrapper.emitted('initiateJiraImport')[0]).toEqual([value]);
});
});
......@@ -9,7 +9,7 @@ const issuesPath = 'gitlab-org/gitlab-test/-/issues';
describe('JiraImportProgress', () => {
let wrapper;
const getGlEmptyStateAttribute = attribute => wrapper.find(GlEmptyState).attributes(attribute);
const getGlEmptyStateProp = attribute => wrapper.find(GlEmptyState).props(attribute);
const getParagraphText = () => wrapper.find('p').text();
......@@ -37,21 +37,21 @@ describe('JiraImportProgress', () => {
});
it('contains illustration', () => {
expect(getGlEmptyStateAttribute('svgpath')).toBe(illustration);
expect(getGlEmptyStateProp('svgPath')).toBe(illustration);
});
it('contains a title', () => {
const title = 'Import in progress';
expect(getGlEmptyStateAttribute('title')).toBe(title);
expect(getGlEmptyStateProp('title')).toBe(title);
});
it('contains button text', () => {
expect(getGlEmptyStateAttribute('primarybuttontext')).toBe('View issues');
expect(getGlEmptyStateProp('primaryButtonText')).toBe('View issues');
});
it('contains button url', () => {
const expected = `${issuesPath}?search=${importProject}`;
expect(getGlEmptyStateAttribute('primarybuttonlink')).toBe(expected);
expect(getGlEmptyStateProp('primaryButtonLink')).toBe(expected);
});
});
......
......@@ -8,7 +8,7 @@ const jiraIntegrationPath = 'gitlab-org/gitlab-test/-/services/jira/edit';
describe('JiraImportSetup', () => {
let wrapper;
const getGlEmptyStateAttribute = attribute => wrapper.find(GlEmptyState).attributes(attribute);
const getGlEmptyStateProp = attribute => wrapper.find(GlEmptyState).props(attribute);
beforeEach(() => {
wrapper = shallowMount(JiraImportSetup, {
......@@ -25,19 +25,19 @@ describe('JiraImportSetup', () => {
});
it('contains illustration', () => {
expect(getGlEmptyStateAttribute('svgpath')).toBe(illustration);
expect(getGlEmptyStateProp('svgPath')).toBe(illustration);
});
it('contains a description', () => {
const description = 'You will first need to set up Jira Integration to use this feature.';
expect(getGlEmptyStateAttribute('description')).toBe(description);
expect(getGlEmptyStateProp('description')).toBe(description);
});
it('contains button text', () => {
expect(getGlEmptyStateAttribute('primarybuttontext')).toBe('Set up Jira Integration');
expect(getGlEmptyStateProp('primaryButtonText')).toBe('Set up Jira Integration');
});
it('contains button link', () => {
expect(getGlEmptyStateAttribute('primarybuttonlink')).toBe(jiraIntegrationPath);
expect(getGlEmptyStateProp('primaryButtonLink')).toBe(jiraIntegrationPath);
});
});
......@@ -45,7 +45,6 @@ describe('LabelsSelect', () => {
labels: mockLabels,
issueUpdateURL: mockUrl,
enableScopedLabels: true,
scopedLabelsDocumentationLink: 'docs-link',
}),
);
});
......@@ -71,10 +70,6 @@ describe('LabelsSelect', () => {
it('generated label item has a gl-label-text class', () => {
expect($labelEl.find('span').hasClass('gl-label-text')).toEqual(true);
});
it('generated label item template does not have gl-label-icon class', () => {
expect($labelEl.find('.gl-label-icon')).toHaveLength(0);
});
});
describe('when scoped label is present', () => {
......@@ -87,7 +82,6 @@ describe('LabelsSelect', () => {
labels: mockScopedLabels,
issueUpdateURL: mockUrl,
enableScopedLabels: true,
scopedLabelsDocumentationLink: 'docs-link',
}),
);
});
......@@ -106,14 +100,6 @@ describe('LabelsSelect', () => {
expect($labelEl.find('a').attr('data-html')).toBe('true');
});
it('generated label item template has question icon', () => {
expect($labelEl.find('i.fa-question-circle')).toHaveLength(1);
});
it('generated label item template has gl-label-icon class', () => {
expect($labelEl.find('.gl-label-icon')).toHaveLength(1);
});
it('generated label item template has correct label styles', () => {
expect($labelEl.find('span.gl-label-text').attr('style')).toBe(
`background-color: ${label.color}; color: ${label.text_color};`,
......@@ -141,7 +127,6 @@ describe('LabelsSelect', () => {
labels: mockScopedLabels2,
issueUpdateURL: mockUrl,
enableScopedLabels: true,
scopedLabelsDocumentationLink: 'docs-link',
}),
);
});
......
......@@ -13,7 +13,7 @@ exports[`AlertWidget Alert firing displays a warning icon and matches snapshot 1
/>
<span
class="text-truncate gl-pl-1"
class="text-truncate gl-pl-1-deprecated-no-really-do-not-use-me"
>
Firing:
alert-label &gt; 42
......@@ -35,7 +35,7 @@ exports[`AlertWidget Alert not firing displays a warning icon and matches snapsh
/>
<span
class="text-truncate gl-pl-1"
class="text-truncate gl-pl-1-deprecated-no-really-do-not-use-me"
>
alert-label &gt; 42
</span>
......
......@@ -38,7 +38,6 @@ export const mockConfig = {
labelsFetchPath: '/gitlab-org/my-project/-/labels.json',
labelsManagePath: '/gitlab-org/my-project/-/labels',
labelsFilterBasePath: '/gitlab-org/my-project/issues',
scopedLabelsDocumentationPath: '/help/user/project/labels.md#scoped-labels-premium',
};
export const mockSuggestedColors = {
......
......@@ -6,6 +6,10 @@ describe 'ActionMailer hooks' do
describe 'smime signature interceptor' do
before do
class_spy(ActionMailer::Base).as_stubbed_const
# rspec-rails calls ActionMailer::Base.deliveries.clear after every test
# https://github.com/rspec/rspec-rails/commit/71c12388e2bad78aaeea6443a393ede78341a7a3
allow(ActionMailer::Base).to receive_message_chain(:deliveries, :clear)
end
it 'is disabled by default' do
......
......@@ -9,6 +9,7 @@ describe Gitlab::JiraImport::IssueSerializer do
let_it_be(:project_label) { create(:label, project: project, title: 'bug') }
let_it_be(:other_project_label) { create(:label, project: project, title: 'feature') }
let_it_be(:group_label) { create(:group_label, group: group, title: 'dev') }
let_it_be(:current_user) { create(:user) }
let(:iid) { 5 }
let(:key) { 'PROJECT-5' }
......@@ -51,7 +52,7 @@ describe Gitlab::JiraImport::IssueSerializer do
let(:params) { { iid: iid } }
subject { described_class.new(project, jira_issue, params).execute }
subject { described_class.new(project, jira_issue, current_user.id, params).execute }
let(:expected_description) do
<<~MD
......@@ -76,7 +77,7 @@ describe Gitlab::JiraImport::IssueSerializer do
state_id: 1,
updated_at: updated_at,
created_at: created_at,
author_id: project.creator_id,
author_id: current_user.id,
assignee_ids: nil,
label_ids: [project_label.id, group_label.id] + Label.reorder(id: :asc).last(2).pluck(:id)
)
......@@ -122,13 +123,13 @@ describe Gitlab::JiraImport::IssueSerializer do
let!(:user) { create(:user, email: 'reporter@example.com') }
it 'defaults the issue author to project creator' do
expect(subject[:author_id]).to eq(project.creator.id)
expect(subject[:author_id]).to eq(current_user.id)
end
end
context 'when reporter does not map to a GitLab user' do
it 'defaults the issue author to project creator' do
expect(subject[:author_id]).to eq(project.creator.id)
expect(subject[:author_id]).to eq(current_user.id)
end
end
......@@ -136,7 +137,7 @@ describe Gitlab::JiraImport::IssueSerializer do
let(:reporter) { nil }
it 'defaults the issue author to project creator' do
expect(subject[:author_id]).to eq(project.creator.id)
expect(subject[:author_id]).to eq(current_user.id)
end
end
......@@ -144,7 +145,7 @@ describe Gitlab::JiraImport::IssueSerializer do
let(:reporter) { double(name: 'Reporter', emailAddress: nil) }
it 'defaults the issue author to project creator' do
expect(subject[:author_id]).to eq(project.creator.id)
expect(subject[:author_id]).to eq(current_user.id)
end
end
end
......
......@@ -6,8 +6,9 @@ describe Gitlab::JiraImport::IssuesImporter do
include JiraServiceHelper
let_it_be(:user) { create(:user) }
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:jira_import) { create(:jira_import_state, project: project) }
let_it_be(:jira_import) { create(:jira_import_state, project: project, user: current_user) }
let_it_be(:jira_service) { create(:jira_service, project: project) }
subject { described_class.new(project) }
......@@ -39,8 +40,16 @@ describe Gitlab::JiraImport::IssuesImporter do
context 'with results returned' do
JiraIssue = Struct.new(:id)
let_it_be(:jira_issue1) { JiraIssue.new(1) }
let_it_be(:jira_issue2) { JiraIssue.new(2) }
let_it_be(:jira_issues) { [JiraIssue.new(1), JiraIssue.new(2)] }
def mock_issue_serializer(count)
serializer = instance_double(Gitlab::JiraImport::IssueSerializer, execute: { key: 'data' })
count.times do |i|
expect(Gitlab::JiraImport::IssueSerializer).to receive(:new)
.with(project, jira_issues[i], current_user.id, { iid: i + 1 }).and_return(serializer)
end
end
context 'when single page of results is returned' do
before do
......@@ -48,13 +57,11 @@ describe Gitlab::JiraImport::IssuesImporter do
end
it 'schedules 2 import jobs' do
expect(subject).to receive(:fetch_issues).and_return([jira_issue1, jira_issue2])
expect(subject).to receive(:fetch_issues).with(0).and_return([jira_issues[0], jira_issues[1]])
expect(Gitlab::JiraImport::ImportIssueWorker).to receive(:perform_async).twice
expect(Gitlab::Cache::Import::Caching).to receive(:set_add).twice.and_call_original
expect(Gitlab::Cache::Import::Caching).to receive(:set_includes?).twice.and_call_original
allow_next_instance_of(Gitlab::JiraImport::IssueSerializer) do |instance|
allow(instance).to receive(:execute).and_return({ key: 'data' })
end
mock_issue_serializer(2)
job_waiter = subject.execute
......@@ -69,13 +76,11 @@ describe Gitlab::JiraImport::IssuesImporter do
end
it 'schedules 3 import jobs' do
expect(subject).to receive(:fetch_issues).with(0).and_return([jira_issue1, jira_issue2])
expect(subject).to receive(:fetch_issues).with(0).and_return([jira_issues[0], jira_issues[1]])
expect(Gitlab::JiraImport::ImportIssueWorker).to receive(:perform_async).twice.times
expect(Gitlab::Cache::Import::Caching).to receive(:set_add).twice.times.and_call_original
expect(Gitlab::Cache::Import::Caching).to receive(:set_includes?).twice.times.and_call_original
allow_next_instance_of(Gitlab::JiraImport::IssueSerializer) do |instance|
allow(instance).to receive(:execute).and_return({ key: 'data' })
end
mock_issue_serializer(2)
job_waiter = subject.execute
......@@ -90,13 +95,11 @@ describe Gitlab::JiraImport::IssuesImporter do
end
it 'schedules 2 import jobs' do
expect(subject).to receive(:fetch_issues).with(0).and_return([jira_issue1, jira_issue1])
expect(subject).to receive(:fetch_issues).with(0).and_return([jira_issues[0], jira_issues[0]])
expect(Gitlab::JiraImport::ImportIssueWorker).to receive(:perform_async).once
expect(Gitlab::Cache::Import::Caching).to receive(:set_add).once.and_call_original
expect(Gitlab::Cache::Import::Caching).to receive(:set_includes?).twice.times.and_call_original
allow_next_instance_of(Gitlab::JiraImport::IssueSerializer) do |instance|
allow(instance).to receive(:execute).and_return({ key: 'data' })
end
mock_issue_serializer(1)
job_waiter = subject.execute
......
......@@ -9,7 +9,7 @@ describe GitlabDanger do
describe '.local_warning_message' do
it 'returns an informational message with rules that can run' do
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changes_size, documentation, frozen_string, duplicate_yarn_dependencies, prettier, eslint, karma, database, commit_messages, telemetry')
expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: changes_size, documentation, frozen_string, duplicate_yarn_dependencies, prettier, eslint, karma, database, commit_messages, telemetry, utility_css')
end
end
......
......@@ -2,6 +2,7 @@
require 'spec_helper'
require_relative 'shared_processing_service.rb'
# require_relative 'shared_processing_service_tests_with_yaml.rb'
describe Ci::PipelineProcessing::AtomicProcessingService do
before do
......@@ -9,6 +10,9 @@ describe Ci::PipelineProcessing::AtomicProcessingService do
end
it_behaves_like 'Pipeline Processing Service'
# TODO: This needs to be enabled. There is a different behavior when using `needs` depending on
# a `manual` job. More info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29405#note_327520605
# it_behaves_like 'Pipeline Processing Service Tests With Yaml'
private
......
......@@ -2,13 +2,16 @@
require 'spec_helper'
require_relative 'shared_processing_service.rb'
require_relative 'shared_processing_service_tests_with_yaml.rb'
describe Ci::PipelineProcessing::LegacyProcessingService do
before do
stub_feature_flags(ci_atomic_processing: false)
stub_feature_flags(ci_composite_status: false)
end
it_behaves_like 'Pipeline Processing Service'
it_behaves_like 'Pipeline Processing Service Tests With Yaml'
private
......
# frozen_string_literal: true
shared_context 'Pipeline Processing Service Tests With Yaml' do
where(:test_file_path) do
Dir.glob(Rails.root.join('spec/services/ci/pipeline_processing/test_cases/*.yml'))
end
with_them do
let(:test_file) { YAML.load_file(test_file_path) }
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:pipeline) { Ci::CreatePipelineService.new(project, user, ref: 'master').execute(:pipeline) }
before do
stub_ci_pipeline_yaml_file(YAML.dump(test_file['config']))
stub_not_protect_default_branch
project.add_developer(user)
end
it 'follows transitions', :sidekiq_inline do
expect(pipeline).to be_persisted
check_expectation(test_file.dig('init', 'expect'))
test_file['transitions'].each do |transition|
event_on_jobs(transition['event'], transition['jobs'])
check_expectation(transition['expect'])
end
end
private
def check_expectation(expectation)
expectation.each do |key, value|
case key
when 'pipeline'
expect(pipeline.reload.status).to eq(value)
when 'stages'
expect(pipeline.stages.pluck(:name, :status).to_h).to eq(value)
when 'jobs'
expect(pipeline.builds.latest.pluck(:name, :status).to_h).to eq(value)
end
end
end
def event_on_jobs(event, job_names)
builds = pipeline.builds.latest.where(name: job_names).to_a
expect(builds.count).to eq(job_names.count) # ensure that we have the same counts
builds.each { |build| build.public_send("#{event}!") }
end
end
end
config:
build:
stage: build
allow_failure: true
script: exit 1
test:
stage: test
when: on_failure
script: exit 0
needs: [build]
deploy:
stage: deploy
script: exit 0
needs: [test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build: pending
test: created
deploy: created
transitions:
- event: drop
jobs: [build]
expect:
pipeline: success
stages:
build: success
test: skipped
deploy: skipped
jobs:
build: failed
test: skipped
deploy: skipped
# TODO: What is the real expected behavior here?
# Is `needs` keyword a requirement indicator or just a helper to build dependency tree?
# How should it behave `when: on_failure` with `needs`?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build:
stage: build
script: exit 1
test:
stage: test
script: exit 0
needs: [build]
deploy:
stage: deploy
script: exit 0
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build: pending
test: created
deploy: created
transitions:
- event: drop
jobs: [build]
expect:
pipeline: failed
stages:
build: failed
test: skipped
deploy: skipped
jobs:
build: failed
test: skipped
deploy: skipped
config:
build:
stage: build
script: exit 1
test:
stage: test
script: exit 0
deploy:
stage: deploy
script: exit 0
needs: [test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build: pending
test: created
deploy: created
transitions:
- event: drop
jobs: [build]
expect:
pipeline: failed
stages:
build: failed
test: skipped
deploy: skipped
jobs:
build: failed
test: skipped
deploy: skipped
config:
build:
stage: build
script: exit 1
test:
stage: test
script: exit 0
deploy:
stage: deploy
script: exit 0
when: always
needs: [test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build: pending
test: created
deploy: created
transitions:
- event: drop
jobs: [build]
expect:
pipeline: running
stages:
build: failed
test: skipped
deploy: pending
jobs:
build: failed
test: skipped
deploy: pending
# TODO: `test` is actually skipped, but we run `deploy`. Should we?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build_1:
stage: build
script: exit 0
build_2:
stage: build
script: exit 1
test:
stage: test
script: exit 0
deploy:
stage: deploy
script: exit 0
needs: [build_1, test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build_1: pending
build_2: pending
test: created
deploy: created
transitions:
- event: success
jobs: [build_1]
expect:
pipeline: running
stages:
build: running
test: created
deploy: created
jobs:
build_1: success
build_2: pending
test: created
deploy: created
- event: drop
jobs: [build_2]
expect:
pipeline: running
stages:
build: failed
test: skipped
deploy: pending
jobs:
build_1: success
build_2: failed
test: skipped
deploy: pending
# TODO: should we run deploy?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build_1:
stage: build
script: exit 0
build_2:
stage: build
script: exit 1
test:
stage: test
script: exit 0
deploy:
stage: deploy
script: exit 0
when: always
needs: [build_1, test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build_1: pending
build_2: pending
test: created
deploy: created
transitions:
- event: success
jobs: [build_1]
expect:
pipeline: running
stages:
build: running
test: created
deploy: created
jobs:
build_1: success
build_2: pending
test: created
deploy: created
- event: drop
jobs: [build_2]
expect:
pipeline: running
stages:
build: failed
test: skipped
deploy: pending
jobs:
build_1: success
build_2: failed
test: skipped
deploy: pending
# TODO: what's the actual expected behavior here?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build:
stage: build
script: exit 1
test:
stage: test
allow_failure: true
script: exit 1
deploy:
stage: deploy
script: exit 0
needs: [test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build: pending
test: created
deploy: created
transitions:
- event: drop
jobs: [build]
expect:
pipeline: failed
stages:
build: failed
test: skipped
deploy: skipped
jobs:
build: failed
test: skipped
deploy: skipped
config:
build:
stage: build
script: exit 1
test:
stage: test
when: always
script: exit 0
needs: [build]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
jobs:
build: pending
test: created
transitions:
- event: drop
jobs: [build]
expect:
pipeline: running
stages:
build: failed
test: pending
jobs:
build: failed
test: pending
# TODO: Should we run `test`?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build:
stage: build
script: exit 1
test:
stage: test
when: on_failure
script: exit 0
needs: [build]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
jobs:
build: pending
test: created
transitions:
- event: drop
jobs: [build]
expect:
pipeline: running
stages:
build: failed
test: pending
jobs:
build: failed
test: pending
# TODO: Should we run `test`?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build:
stage: build
script: exit 0
test:
stage: test
when: on_failure
script: exit 0
needs: [build]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
jobs:
build: pending
test: created
transitions:
- event: success
jobs: [build]
expect:
pipeline: success
stages:
build: success
test: skipped
jobs:
build: success
test: skipped
# TODO: Should we run `test`?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build_1:
stage: build
script: exit 0
build_2:
stage: build
script: exit 0
test:
stage: test
script: exit 0
when: on_failure
deploy:
stage: deploy
script: exit 0
needs: [build_1, test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build_1: pending
build_2: pending
test: created
deploy: created
transitions:
- event: success
jobs: [build_1, build_2]
expect:
pipeline: running
stages:
build: success
test: skipped
deploy: pending
jobs:
build_1: success
build_2: success
test: skipped
deploy: pending
- event: success
jobs: [deploy]
expect:
pipeline: success
stages:
build: success
test: skipped
deploy: success
jobs:
build_1: success
build_2: success
test: skipped
deploy: success
# TODO: should we run deploy?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
build_1:
stage: build
script: exit 0
build_2:
stage: build
script: exit 0
test:
stage: test
script: exit 0
when: on_failure
deploy:
stage: deploy
script: exit 0
when: always
needs: [build_1, test]
init:
expect:
pipeline: pending
stages:
build: pending
test: created
deploy: created
jobs:
build_1: pending
build_2: pending
test: created
deploy: created
transitions:
- event: success
jobs: [build_1, build_2]
expect:
pipeline: running
stages:
build: success
test: skipped
deploy: pending
jobs:
build_1: success
build_2: success
test: skipped
deploy: pending
- event: success
jobs: [deploy]
expect:
pipeline: success
stages:
build: success
test: skipped
deploy: success
jobs:
build_1: success
build_2: success
test: skipped
deploy: success
# TODO: should we run deploy?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
config:
test:
stage: test
allow_failure: true
script: exit 1
deploy:
stage: deploy
script: exit 0
needs: [test]
init:
expect:
pipeline: pending
stages:
test: pending
deploy: created
jobs:
test: pending
deploy: created
transitions:
- event: drop
jobs: [test]
expect:
pipeline: pending
stages:
test: success
deploy: pending
jobs:
test: failed
deploy: pending
- event: success
jobs: [deploy]
expect:
pipeline: success
stages:
test: success
deploy: success
jobs:
test: failed
deploy: success
config:
test:
stage: test
when: manual
allow_failure: false
script: exit 0
deploy:
stage: deploy
script: exit 0
needs: [test]
init:
expect:
pipeline: manual
stages:
test: manual
deploy: created
jobs:
test: manual
deploy: created
transitions:
- event: enqueue
jobs: [test]
expect:
pipeline: manual
stages:
test: manual
deploy: created
jobs:
test: pending
deploy: created
- event: run
jobs: [test]
expect:
pipeline: running
stages:
test: running
deploy: created
jobs:
test: running
deploy: created
- event: success
jobs: [test]
expect:
pipeline: running
stages:
test: success
deploy: pending
jobs:
test: success
deploy: pending
- event: success
jobs: [deploy]
expect:
pipeline: success
stages:
test: success
deploy: success
jobs:
test: success
deploy: success
config:
test:
stage: test
when: manual
allow_failure: true
script: exit 1
deploy:
stage: deploy
script: exit 0
needs: [test]
init:
expect:
pipeline: created
stages:
test: skipped
deploy: created
jobs:
test: manual
deploy: created
transitions:
- event: enqueue
jobs: [test]
expect:
pipeline: pending
stages:
test: running
deploy: created
jobs:
test: pending
deploy: created
- event: run
jobs: [test]
expect:
pipeline: running
stages:
test: running
deploy: created
jobs:
test: running
deploy: created
- event: drop
jobs: [test]
expect:
pipeline: running
stages:
test: success
deploy: pending
jobs:
test: failed
deploy: pending
# TOOD: should we run deploy?
# Further discussions: https://gitlab.com/gitlab-org/gitlab/-/issues/213080
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册