Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-15 15:09:07 +00:00
parent c8b7a349bc
commit f5c3f32975
134 changed files with 1332 additions and 400 deletions

View File

@ -201,7 +201,6 @@ linters:
- 'app/views/projects/imports/new.html.haml'
- 'app/views/projects/imports/show.html.haml'
- 'app/views/projects/issues/_new_branch.html.haml'
- 'app/views/projects/issues/import_csv/_modal.html.haml'
- 'app/views/projects/issues/show.html.haml'
- 'app/views/projects/jobs/_header.html.haml'
- 'app/views/projects/jobs/_table.html.haml'

View File

@ -13,7 +13,7 @@
],
"rules":{
"max-nesting-depth": [
5,
3,
{
"ignoreAtRules":[
"each",
@ -24,7 +24,7 @@
"severity":"warning"
}
],
"selector-max-compound-selectors":[6, { "severity": "warning" }],
"selector-max-compound-selectors":[3, { "severity": "warning" }],
"stylelint-gitlab/utility-classes":[true,{ "severity": "warning" }],
"declaration-block-no-duplicate-properties": [
true,

View File

@ -1 +1 @@
2518b0d69dfabb6b0200e54481ed3a396fc5c9f5
0fc3e28a00fe119679257707dadfb1b9e3354b28

View File

@ -2,6 +2,9 @@ import { memoize } from 'lodash';
import AccessorUtilities from '~/lib/utils/accessor';
import { s__ } from '~/locale';
const isCustomizable = (command) =>
'customizable' in command ? Boolean(command.customizable) : true;
export const LOCAL_STORAGE_KEY = 'gl-keyboard-shortcuts-customizations';
/**
@ -81,7 +84,7 @@ export const keybindingGroups = [GLOBAL_SHORTCUTS_GROUP, WEB_IDE_GROUP];
* Mousetrap.bind(keysFor(TOGGLE_PERFORMANCE_BAR), handler);
*/
export const keysFor = (command) => {
if (command.customizable === false) {
if (!isCustomizable(command)) {
// if the command is defined with `customizable: false`,
// don't allow this command to be customized.
return command.defaultKeys;

View File

@ -104,6 +104,9 @@ export default () => {
? parseInt($boardApp.dataset.boardWeight, 10)
: null,
scopedLabelsAvailable: parseBoolean($boardApp.dataset.scopedLabels),
milestoneListsAvailable: parseBoolean($boardApp.dataset.milestoneListsAvailable),
assigneeListsAvailable: parseBoolean($boardApp.dataset.assigneeListsAvailable),
iterationListsAvailable: parseBoolean($boardApp.dataset.iterationListsAvailable),
},
store,
apolloProvider,

View File

@ -134,7 +134,7 @@ export default {
createIssueList: (
{ state, commit, dispatch, getters },
{ backlog, labelId, milestoneId, assigneeId },
{ backlog, labelId, milestoneId, assigneeId, iterationId },
) => {
const { boardId } = state;
@ -154,6 +154,7 @@ export default {
labelId,
milestoneId,
assigneeId,
iterationId,
},
})
.then(({ data }) => {

View File

@ -575,7 +575,7 @@ const boardsStore = {
},
saveList(list) {
const entity = list.label || list.assignee || list.milestone;
const entity = list.label || list.assignee || list.milestone || list.iteration;
let entityType = '';
if (list.label) {
entityType = 'label_id';
@ -583,6 +583,8 @@ const boardsStore = {
entityType = 'assignee_id';
} else if (IS_EE && list.milestone) {
entityType = 'milestone_id';
} else if (IS_EE && list.iteration) {
entityType = 'iteration_id';
}
return this.createList(entity.id, entityType)

View File

@ -126,7 +126,7 @@ export default {
v-bind="field"
/>
<jira-issues-fields
v-if="isJira"
v-if="isJira && !isInstanceOrGroupLevel"
:key="`${currentKey}-jira-issues-fields`"
v-bind="propsSource.jiraIssuesProps"
/>

View File

@ -7,6 +7,8 @@ import {
GlTooltipDirective,
GlModalDirective,
} from '@gitlab/ui';
import { __ } from '~/locale';
import { ISSUABLE_TYPE } from '../constants';
import CsvExportModal from './csv_export_modal.vue';
import CsvImportModal from './csv_import_modal.vue';
@ -25,6 +27,9 @@ export default {
GlModal: GlModalDirective,
},
inject: {
issuableType: {
default: ISSUABLE_TYPE.issues,
},
showExportButton: {
default: false,
},
@ -40,6 +45,9 @@ export default {
projectImportJiraPath: {
default: null,
},
showLabel: {
default: false,
},
},
computed: {
exportModalId() {
@ -48,7 +56,17 @@ export default {
importModalId() {
return `${this.issuableType}-import-modal`;
},
importButtonText() {
return this.showLabel ? this.$options.importIssuesText : null;
},
importButtonTooltipText() {
return this.showLabel ? null : this.$options.importIssuesText;
},
importButtonIcon() {
return this.showLabel ? null : 'import';
},
},
importIssuesText: __('Import issues'),
};
</script>
@ -65,9 +83,11 @@ export default {
/>
<gl-dropdown
v-if="showImportButton"
v-gl-tooltip.hover="__('Import issues')"
v-gl-tooltip.hover="importButtonTooltipText"
data-qa-selector="import_issues_dropdown"
data-testid="import-csv-dropdown"
icon="import"
:text="importButtonText"
:icon="importButtonIcon"
>
<gl-dropdown-item v-gl-modal="importModalId" data-testid="import-csv-link">{{
__('Import CSV')

View File

@ -19,6 +19,7 @@ export default () => {
canEdit,
projectImportJiraPath,
maxAttachmentSize,
showLabel,
} = el.dataset;
return new Vue({
@ -35,6 +36,7 @@ export default () => {
canEdit: parseBoolean(canEdit),
projectImportJiraPath,
maxAttachmentSize,
showLabel,
},
render(h) {
return h(ImportExportButtons);

View File

@ -1,6 +1,8 @@
<script>
import { GlLink } from '@gitlab/ui';
import TaskList from '~/task_list';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import IssuableDescription from './issuable_description.vue';
@ -40,6 +42,11 @@ export default {
type: Boolean,
required: true,
},
enableTaskList: {
type: Boolean,
required: false,
default: false,
},
editFormVisible: {
type: Boolean,
required: true,
@ -56,6 +63,16 @@ export default {
type: String,
required: true,
},
taskListUpdatePath: {
type: String,
required: false,
default: '',
},
taskListLockVersion: {
type: Number,
required: false,
default: 0,
},
},
computed: {
isUpdated() {
@ -65,7 +82,50 @@ export default {
return this.issuable.updatedBy;
},
},
watch: {
/**
* When user switches between view and edit modes,
* taskList instance becomes invalid so whenever
* view mode is rendered, we need to re-initialize
* taskList to ensure the behaviour functional.
*/
editFormVisible(value) {
if (!value) {
this.$nextTick(() => {
this.initTaskList();
});
}
},
},
mounted() {
if (this.enableEdit && this.enableTaskList) {
this.initTaskList();
}
},
methods: {
initTaskList() {
this.taskList = new TaskList({
/**
* We have hard-coded dataType to `issue`
* as currently only `issue` types can handle
* task-lists, however, we can still use
* task lists in Issue, Test Cases and Incidents
* as all of those are derived from `issue`.
*/
dataType: 'issue',
fieldName: 'description',
lockVersion: this.taskListLockVersion,
selector: '.js-detail-page-description',
onSuccess: this.handleTaskListUpdateSuccess.bind(this),
onError: this.handleTaskListUpdateFailure.bind(this),
});
},
handleTaskListUpdateSuccess(updatedIssuable) {
this.$emit('task-list-update-success', updatedIssuable);
},
handleTaskListUpdateFailure() {
this.$emit('task-list-update-failure');
},
handleKeydownTitle(e, issuableMeta) {
this.$emit('keydown-title', e, issuableMeta);
},
@ -78,7 +138,7 @@ export default {
<template>
<div class="issue-details issuable-details">
<div class="detail-page-description content-block">
<div class="detail-page-description js-detail-page-description content-block">
<issuable-edit-form
v-if="editFormVisible"
:issuable="issuable"
@ -106,7 +166,13 @@ export default {
<slot name="status-badge"></slot>
</template>
</issuable-title>
<issuable-description v-if="issuable.descriptionHtml" :issuable="issuable" />
<issuable-description
v-if="issuable.descriptionHtml"
:issuable="issuable"
:enable-task-list="enableTaskList"
:can-edit="enableEdit"
:task-list-update-path="taskListUpdatePath"
/>
<small v-if="isUpdated" class="edited-text gl-font-sm!">
{{ __('Edited') }}
<time-ago-tooltip :time="issuable.updatedAt" tooltip-placement="bottom" />

View File

@ -12,6 +12,18 @@ export default {
type: Object,
required: true,
},
enableTaskList: {
type: Boolean,
required: true,
},
canEdit: {
type: Boolean,
required: true,
},
taskListUpdatePath: {
type: String,
required: true,
},
},
mounted() {
this.renderGFM();
@ -25,7 +37,16 @@ export default {
</script>
<template>
<div class="description">
<div class="description" :class="{ 'js-task-list-container': canEdit && enableTaskList }">
<div ref="gfmContainer" v-safe-html="issuable.descriptionHtml" class="md"></div>
<textarea
v-if="issuable.description && enableTaskList"
ref="textarea"
:value="issuable.description"
:data-update-url="taskListUpdatePath"
class="gl-display-none js-task-list-field"
dir="auto"
>
</textarea>
</div>
</template>

View File

@ -3,6 +3,7 @@ import { GlIcon, GlButton, GlTooltipDirective, GlAvatarLink, GlAvatarLabeled } f
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { isExternal } from '~/lib/utils/url_utility';
import { n__, sprintf } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
@ -45,6 +46,11 @@ export default {
required: false,
default: false,
},
taskCompletionStatus: {
type: Object,
required: false,
default: null,
},
},
computed: {
authorId() {
@ -53,6 +59,18 @@ export default {
isAuthorExternal() {
return isExternal(this.author.webUrl);
},
taskStatusString() {
const { count, completedCount } = this.taskCompletionStatus;
return sprintf(
n__(
'%{completedCount} of %{count} task completed',
'%{completedCount} of %{count} tasks completed',
count,
),
{ completedCount, count },
);
},
},
mounted() {
this.toggleSidebarButtonEl = document.querySelector('.js-toggle-right-sidebar-button');
@ -74,8 +92,8 @@ export default {
<gl-icon v-if="statusIcon" :name="statusIcon" class="d-block d-sm-none" />
<span class="d-none d-sm-block"><slot name="status-badge"></slot></span>
</div>
<div class="issuable-meta gl-display-flex gl-align-items-center">
<div class="gl-display-inline-block">
<div class="issuable-meta gl-display-flex gl-align-items-center d-md-inline-block">
<div v-if="blocked || confidential" class="gl-display-inline-block">
<div v-if="blocked" data-testid="blocked" class="issuable-warning-icon inline">
<gl-icon name="lock" :aria-label="__('Blocked')" />
</div>
@ -95,13 +113,13 @@ export default {
:data-name="author.name"
:href="author.webUrl"
target="_blank"
class="js-user-link gl-ml-2"
class="js-user-link gl-vertical-align-middle gl-ml-2"
>
<gl-avatar-labeled
:size="24"
:src="author.avatarUrl"
:label="author.name"
class="d-none d-sm-inline-flex gl-ml-1"
class="d-none d-sm-inline-flex gl-mx-1"
>
<template #meta>
<gl-icon v-if="isAuthorExternal" name="external-link" />
@ -109,6 +127,12 @@ export default {
</gl-avatar-labeled>
<strong class="author d-sm-none d-inline">@{{ author.username }}</strong>
</gl-avatar-link>
<span
v-if="taskCompletionStatus"
data-testid="task-status"
class="gl-display-none gl-md-display-block gl-lg-display-inline-block"
>{{ taskStatusString }}</span
>
</div>
<gl-button
data-testid="sidebar-toggle"

View File

@ -42,6 +42,11 @@ export default {
required: false,
default: true,
},
enableTaskList: {
type: Boolean,
required: false,
default: false,
},
editFormVisible: {
type: Boolean,
required: false,
@ -62,6 +67,21 @@ export default {
required: false,
default: '',
},
taskCompletionStatus: {
type: Object,
required: false,
default: null,
},
taskListUpdatePath: {
type: String,
required: false,
default: '',
},
taskListLockVersion: {
type: Number,
required: false,
default: 0,
},
},
methods: {
handleKeydownTitle(e, issuableMeta) {
@ -83,6 +103,7 @@ export default {
:confidential="issuable.confidential"
:created-at="issuable.createdAt"
:author="issuable.author"
:task-completion-status="taskCompletionStatus"
>
<template #status-badge>
<slot name="status-badge"></slot>
@ -99,11 +120,16 @@ export default {
:enable-edit="enableEdit"
:enable-autocomplete="enableAutocomplete"
:enable-autosave="enableAutosave"
:enable-task-list="enableTaskList"
:edit-form-visible="editFormVisible"
:show-field-title="showFieldTitle"
:description-preview-path="descriptionPreviewPath"
:description-help-path="descriptionHelpPath"
:task-list-update-path="taskListUpdatePath"
:task-list-lock-version="taskListLockVersion"
@edit-issuable="$emit('edit-issuable', $event)"
@task-list-update-success="$emit('task-list-update-success', $event)"
@task-list-update-failure="$emit('task-list-update-failure')"
@keydown-title="handleKeydownTitle"
@keydown-description="handleKeydownDescription"
>

View File

@ -1095,3 +1095,25 @@ export const getStartOfDay = (date, { utc = false } = {}) => {
return new Date(cloneValue);
};
/**
* Returns the start of the current week against the provide date
*
* @param {Date} date The current date instance to calculate against
* @param {Object} [options={}] Additional options for this calculation
* @param {boolean} [options.utc=false] Performs the calculation using UTC time.
* If `true`, the time returned will be midnight UTC. If `false` (the default)
* the time returned will be midnight in the user's local time.
*
* @returns {Date} A new `Date` object that represents the start of the current week
* of the provided date
*/
export const getStartOfWeek = (date, { utc = false } = {}) => {
const cloneValue = utc
? new Date(date.setUTCHours(0, 0, 0, 0))
: new Date(date.setHours(0, 0, 0, 0));
const diff = cloneValue.getDate() - cloneValue.getDay() + (cloneValue.getDay() === 0 ? -6 : 1);
return new Date(date.setDate(diff));
};

View File

@ -234,8 +234,7 @@ export default class BranchGraph {
appendAnchor(x, y, commit) {
const { r, top, options } = this;
const anchor = r
.circle(x, y, 10)
r.circle(x, y, 10)
.attr({
fill: '#000',
opacity: 0,
@ -245,13 +244,14 @@ export default class BranchGraph {
.hover(
function () {
this.tooltip = r.commitTooltip(x + 5, y, commit);
return top.push(this.tooltip.insertBefore(this));
top.push(this.tooltip.insertBefore(this));
return this.tooltip.toFront();
},
function () {
top.remove(this.tooltip);
return this.tooltip && this.tooltip.remove() && delete this.tooltip;
},
);
return top.push(anchor);
}
drawDot(x, y, commit) {

View File

@ -17,12 +17,10 @@ function mountRemoveMemberModal() {
});
}
document.addEventListener('DOMContentLoaded', () => {
mountRemoveMemberModal();
mountRemoveMemberModal();
new ProjectsList(); // eslint-disable-line no-new
new ProjectsList(); // eslint-disable-line no-new
document
.querySelectorAll('.js-namespace-select')
.forEach((dropdown) => new NamespaceSelect({ dropdown }));
});
document
.querySelectorAll('.js-namespace-select')
.forEach((dropdown) => new NamespaceSelect({ dropdown }));

View File

@ -1,6 +1,4 @@
import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
const callout = document.querySelector('.js-service-templates-deprecated');
PersistentUserCallout.factory(callout);
});
const callout = document.querySelector('.js-service-templates-deprecated');
PersistentUserCallout.factory(callout);

View File

@ -80,7 +80,7 @@ export default {
this.isLoading = false;
})
.catch(() => {
this.$refs.stageGlDropdown.hide();
this.$refs.dropdown.hide();
this.isLoading = false;
Flash(__('Something went wrong on our end.'));
@ -91,7 +91,7 @@ export default {
},
pipelineActionRequestComplete() {
// close the dropdown in MR widget
this.$refs.stageGlDropdown.hide();
this.$refs.dropdown.hide();
// warn the pipelines table to update
this.$emit('pipelineActionRequestComplete');
@ -102,7 +102,7 @@ export default {
<template>
<gl-dropdown
ref="stageGlDropdown"
ref="dropdown"
v-gl-tooltip.hover
data-testid="mini-pipeline-graph-dropdown"
:title="stage.title"

View File

@ -0,0 +1,84 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import { referenceQueries } from '~/sidebar/constants';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
export default {
i18n: {
copyReference: __('Copy reference'),
text: __('Reference'),
},
components: {
ClipboardButton,
GlLoadingIcon,
},
inject: ['fullPath', 'iid'],
props: {
issuableType: {
required: true,
type: String,
},
},
data() {
return {
reference: '',
};
},
apollo: {
reference: {
query() {
return referenceQueries[this.issuableType].query;
},
variables() {
return {
fullPath: this.fullPath,
iid: this.iid,
};
},
update(data) {
return data.workspace?.issuable?.reference || '';
},
error(error) {
this.$emit('fetch-error', {
message: __('An error occurred while fetching reference'),
error,
});
},
},
},
computed: {
isLoading() {
return this.$apollo.queries.reference.loading;
},
},
};
</script>
<template>
<div class="sub-block">
<clipboard-button
v-if="!isLoading"
:title="$options.i18n.copyReference"
:text="reference"
category="tertiary"
css-class="sidebar-collapsed-icon dont-change-state"
tooltip-placement="left"
/>
<div class="gl-display-flex gl-align-items-center gl-justify-between gl-mb-2 hide-collapsed">
<span class="gl-overflow-hidden gl-text-overflow-ellipsis gl-white-space-nowrap">
{{ $options.i18n.text }}: {{ reference }}
<gl-loading-icon v-if="isLoading" inline :label="$options.i18n.text" />
</span>
<clipboard-button
v-if="!isLoading"
:title="$options.i18n.copyReference"
:text="reference"
size="small"
category="tertiary"
css-class="gl-mr-1"
tooltip-placement="left"
/>
</div>
</div>
</template>

View File

@ -1,6 +1,8 @@
import { IssuableType } from '~/issue_show/constants';
import epicConfidentialQuery from '~/sidebar/queries/epic_confidential.query.graphql';
import issueConfidentialQuery from '~/sidebar/queries/issue_confidential.query.graphql';
import issueReferenceQuery from '~/sidebar/queries/issue_reference.query.graphql';
import mergeRequestReferenceQuery from '~/sidebar/queries/merge_request_reference.query.graphql';
import updateEpicMutation from '~/sidebar/queries/update_epic_confidential.mutation.graphql';
import updateIssueConfidentialMutation from '~/sidebar/queries/update_issue_confidential.mutation.graphql';
import getIssueParticipants from '~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql';
@ -31,3 +33,12 @@ export const confidentialityQueries = {
mutation: updateEpicMutation,
},
};
export const referenceQueries = {
[IssuableType.Issue]: {
query: issueReferenceQuery,
},
[IssuableType.MergeRequest]: {
query: mergeRequestReferenceQuery,
},
};

View File

@ -2,6 +2,7 @@ import $ from 'jquery';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createFlash from '~/flash';
import { IssuableType } from '~/issue_show/constants';
import {
isInIssuePage,
isInDesignPage,
@ -10,6 +11,7 @@ import {
} from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import SidebarReferenceWidget from '~/sidebar/components/reference/sidebar_reference_widget.vue';
import { apolloProvider } from '~/sidebar/graphql';
import Translate from '../vue_shared/translate';
import SidebarAssignees from './components/assignees/sidebar_assignees.vue';
@ -75,7 +77,9 @@ function mountAssigneesComponent(mediator) {
field: el.dataset.field,
signedIn: el.hasAttribute('data-signed-in'),
issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage() ? 'issue' : 'merge_request',
isInIssuePage() || isInIncidentPage() || isInDesignPage()
? IssuableType.Issue
: IssuableType.MergeRequest,
assigneeAvailabilityStatus,
},
}),
@ -156,7 +160,41 @@ function mountConfidentialComponent() {
createElement('sidebar-confidentiality-widget', {
props: {
issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage() ? 'issue' : 'merge_request',
isInIssuePage() || isInIncidentPage() || isInDesignPage()
? IssuableType.Issue
: IssuableType.MergeRequest,
},
}),
});
}
function mountReferenceComponent() {
const el = document.getElementById('js-reference-entry-point');
if (!el) {
return;
}
const { fullPath, iid } = getSidebarOptions();
// eslint-disable-next-line no-new
new Vue({
el,
apolloProvider,
components: {
SidebarReferenceWidget,
},
provide: {
iid: String(iid),
fullPath,
},
render: (createElement) =>
createElement('sidebar-reference-widget', {
props: {
issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage()
? IssuableType.Issue
: IssuableType.MergeRequest,
},
}),
});
@ -307,6 +345,7 @@ export function mountSidebar(mediator) {
mountAssigneesComponent(mediator);
mountReviewersComponent(mediator);
mountConfidentialComponent(mediator);
mountReferenceComponent(mediator);
mountLockComponent();
mountParticipantsComponent(mediator);
mountSubscriptionsComponent(mediator);

View File

@ -0,0 +1,10 @@
query issueReference($fullPath: ID!, $iid: String) {
workspace: project(fullPath: $fullPath) {
__typename
issuable: issue(iid: $iid) {
__typename
id
reference(full: true)
}
}
}

View File

@ -0,0 +1,10 @@
query mergeRequestReference($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
__typename
issuable: mergeRequest(iid: $iid) {
__typename
id
reference(full: true)
}
}
}

View File

@ -80,7 +80,7 @@ export default {
<template>
<gl-button
v-gl-tooltip.hover.blur="{
v-gl-tooltip.hover.blur.viewport="{
placement: tooltipPlacement,
container: tooltipContainer,
boundary: tooltipBoundary,

View File

@ -4,7 +4,6 @@
margin-bottom: 0;
min-height: $header-height;
border: 0;
border-bottom: 1px solid $border-color;
position: fixed;
top: 0;
left: 0;

View File

@ -1031,6 +1031,7 @@ body.gl-dark .logo-text svg {
}
body.gl-dark .navbar-gitlab {
background-color: #2e2e2e;
box-shadow: 0 1px 0 0 var(--gray-100);
}
body.gl-dark .navbar-gitlab .navbar-sub-nav li.active > a,
body.gl-dark .navbar-gitlab .navbar-sub-nav li.active > button,

View File

@ -89,6 +89,7 @@ body {
.navbar-gitlab {
background-color: var(--gray-50);
box-shadow: 0 1px 0 0 var(--gray-100);
.navbar-sub-nav,
.navbar-nav {

View File

@ -10,9 +10,10 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp
end
def publish(_result = nil)
return unless should_track? # don't track events for excluded contexts
track(:assignment) # track that we've assigned a variant for this context
# push the experiment data to the client
begin
Gon.push({ experiment: { name => signature } }, true) # push the experiment data to the client
rescue NoMethodError

View File

@ -17,18 +17,18 @@ module Emails
send_from_user_email: false,
sender_name: @project.service_desk_setting&.outgoing_name
)
options = service_desk_options(email_sender, 'thank_you')
options = service_desk_options(email_sender, 'thank_you', @issue.external_author)
.merge(subject: "Re: #{subject_base}")
mail_new_thread(@issue, options)
end
def service_desk_new_note_email(issue_id, note_id)
def service_desk_new_note_email(issue_id, note_id, recipient)
@note = Note.find(note_id)
setup_service_desk_mail(issue_id)
email_sender = sender(@note.author_id)
options = service_desk_options(email_sender, 'new_note')
options = service_desk_options(email_sender, 'new_note', recipient)
.merge(subject: subject_base)
mail_answer_thread(@issue, options)
@ -44,10 +44,10 @@ module Emails
@sent_notification = SentNotification.record(@issue, @support_bot.id, reply_key)
end
def service_desk_options(email_sender, email_type)
def service_desk_options(email_sender, email_type, recipient)
{
from: email_sender,
to: @issue.external_author
to: recipient
}.tap do |options|
next unless template_body = template_content(email_type)

View File

@ -181,7 +181,7 @@ class NotifyPreview < ActionMailer::Preview
cleanup do
note = create_note(noteable_type: 'Issue', noteable_id: issue.id, note: 'Issue note content')
Notify.service_desk_new_note_email(issue.id, note.id).message
Notify.service_desk_new_note_email(issue.id, note.id, 'someone@gitlab.com').message
end
end

View File

@ -438,7 +438,11 @@ class Issue < ApplicationRecord
issue_type_supports?(:assignee)
end
def email_participants_downcase
def email_participants_emails
issue_email_participants.pluck(:email)
end
def email_participants_emails_downcase
issue_email_participants.pluck(IssueEmailParticipant.arel_table[:email].lower)
end

View File

@ -70,3 +70,5 @@ class UsersStatistics < ApplicationRecord
end
end
end
UsersStatistics.prepend_if_ee('EE::UsersStatistics')

View File

@ -369,18 +369,19 @@ class NotificationService
end
def send_service_desk_notification(note)
return unless Gitlab::ServiceDesk.supported?
return unless note.noteable_type == 'Issue'
issue = note.noteable
recipients = issue.email_participants_emails
return unless recipients.any?
support_bot = User.support_bot
recipients.delete(issue.external_author) if note.author == support_bot
return unless issue.external_author.present?
return unless issue.project.service_desk_enabled?
return if note.author == support_bot
return unless issue.subscribed?(support_bot, issue.project)
mailer.service_desk_new_note_email(issue.id, note.id).deliver_later
recipients.each do |recipient|
mailer.service_desk_new_note_email(issue.id, note.id, recipient).deliver_later
end
end
# Notify users when a new release is created

View File

@ -1 +0,0 @@
= s_('AdminArea|Active users')

View File

@ -50,10 +50,11 @@
= s_('AdminArea|Bots')
%td.p-3.text-right
= @users_statistics&.bots.to_i
= render_if_exists 'admin/dashboard/billable_users_row'
%tr.bg-gray-light.gl-text-gray-900
%td.p-3
%strong
= render_if_exists 'admin/dashboard/billable_users_text'
= s_('AdminArea|Active users')
%td.p-3.text-right
%strong
= @users_statistics&.active.to_i

View File

@ -25,4 +25,4 @@
.col-md-4
= password_field_tag :personal_access_token, '', class: 'form-control gl-mr-3', placeholder: _('Personal Access Token'), size: 40
.form-actions
= submit_tag _('List your Bitbucket Server repositories'), class: 'btn btn-success'
= submit_tag _('List your Bitbucket Server repositories'), class: 'gl-button btn btn-confirm'

View File

@ -22,4 +22,4 @@
.col-md-4
= password_field_tag :password, nil, class: 'form-control'
.form-actions
= submit_tag _('Continue to the next step'), class: 'btn btn-success'
= submit_tag _('Continue to the next step'), class: 'gl-button btn btn-confirm'

View File

@ -40,4 +40,4 @@
scope: :all, email_user: true, selected: user[:gitlab_user])
.form-actions
= submit_tag _('Continue to the next step'), class: 'btn btn-success'
= submit_tag _('Continue to the next step'), class: 'gl-button btn btn-confirm'

View File

@ -19,4 +19,4 @@
.col-sm-4
= text_field_tag :personal_access_token, nil, class: 'form-control'
.form-actions
= submit_tag _('List Your Gitea Repositories'), class: 'btn btn-success'
= submit_tag _('List Your Gitea Repositories'), class: 'gl-button btn btn-confirm'

View File

@ -10,7 +10,7 @@
= import_github_authorize_message
- if github_import_configured? && !has_ci_cd_only_params?
= link_to status_import_github_path, class: 'btn btn-success gl-button' do
= link_to status_import_github_path, class: 'gl-button btn btn-confirm' do
= sprite_icon('github', css_class: 'gl-mr-2')
= title

View File

@ -19,5 +19,5 @@
= link_to sprite_icon('question-o'), help_page_path('user/project/import/manifest')
.gl-mb-3
= submit_tag _('List available repositories'), class: 'gl-button btn btn-success'
= submit_tag _('List available repositories'), class: 'gl-button btn btn-confirm'
= link_to _('Cancel'), new_project_path, class: 'gl-button btn btn-default btn-cancel'

View File

@ -24,4 +24,4 @@
.col-md-4
= password_field_tag :api_token, params[:api_token], class: 'form-control gl-mr-3', placeholder: _('Personal Access Token'), size: 40
.form-actions
= submit_tag _('Import tasks'), class: 'btn btn-success'
= submit_tag _('Import tasks'), class: 'gl-button btn btn-confirm'

View File

@ -1,17 +0,0 @@
- type = local_assigns.fetch(:type, :icon)
- can_edit = can?(current_user, :admin_project, @project)
.dropdown.btn-group
%button.btn.gl-button.rounded-right.btn-default.btn-icon.text-center{ class: ('has-tooltip' if type == :icon), title: (_('Import issues') if type == :icon),
data: { toggle: 'dropdown', qa_selector: 'import_issues_button' }, 'aria-label' => _('Import issues'), 'aria-haspopup' => 'true', 'aria-expanded' => 'false' }
- if type == :icon
= sprite_icon('import')
- else
= _('Import issues')
%ul.dropdown-menu
%li
%button{ data: { toggle: 'modal', target: '.issues-import-modal' } }
= _('Import CSV')
- if can_edit
%li{ data: { qa_selector: 'import_from_jira_link' } }
= link_to _('Import from Jira'), project_import_jira_path(@project)

View File

@ -1,22 +0,0 @@
.issues-import-modal.modal
.modal-dialog
.modal-content
= form_tag import_csv_namespace_project_issues_path, multipart: true do
.modal-header
%h4.gl-m-0
= _('Import issues')
%a.close{ href: '#', 'data-dismiss' => 'modal' } ×
.modal-body
.modal-text
%p
= _("Your issues will be imported in the background. Once finished, you'll get a confirmation email.")
.form-group
= label_tag :file, _('Upload CSV file'), class: 'label-bold'
%div
= file_field_tag :file, accept: '.csv,text/csv', required: true
%p.text-secondary
= _('It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected.')
= _('The maximum file size allowed is %{size}.') % { size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes) }
.modal-footer
%button{ type: 'submit', class: 'gl-button btn btn-success', title: _('Import issues'), data: { track_label: "export_issues_csv", track_event: "click_button", track_value: ""} }
= _('Import issues')

View File

@ -21,7 +21,7 @@
%span.badge.badge-danger
= s_('GitLabPages|Expired')
%div
= link_to s_('GitLabPages|Edit'), project_pages_domain_path(@project, domain), class: "btn gl-button btn-sm btn-grouped btn-success btn-inverted"
= link_to s_('GitLabPages|Edit'), project_pages_domain_path(@project, domain), class: "btn gl-button btn-sm btn-grouped btn-confirm btn-inverted"
= link_to s_('GitLabPages|Remove'), project_pages_domain_path(@project, domain), data: { confirm: s_('GitLabPages|Are you sure?')}, method: :delete, class: "btn gl-button btn-danger btn-sm btn-grouped"
- if domain.needs_verification?
%li.list-group-item.bs-callout-warning

View File

@ -11,4 +11,4 @@
= s_('GitLabPages|Force HTTPS (requires valid certificates)')
.gl-mt-3
= f.submit s_('GitLabPages|Save'), class: 'btn btn-success gl-button'
= f.submit s_('GitLabPages|Save'), class: 'btn btn-confirm gl-button'

View File

@ -5,7 +5,7 @@
= s_('GitLabPages|Pages')
- if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https)
= link_to new_project_pages_domain_path(@project), class: 'btn gl-button btn-success float-right', title: s_('GitLabPages|New Domain') do
= link_to new_project_pages_domain_path(@project), class: 'btn gl-button btn-confirm float-right', title: s_('GitLabPages|New Domain') do
= s_('GitLabPages|New Domain')
%p.light

View File

@ -7,6 +7,6 @@
= form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions
= f.submit _('Create New Domain'), class: "btn btn-success"
= f.submit _('Create New Domain'), class: "gl-button btn btn-confirm"
.float-right
= link_to _('Cancel'), project_pages_path(@project), class: 'btn btn-cancel'
= link_to _('Cancel'), project_pages_path(@project), class: 'gl-button btn btn-default btn-cancel'

View File

@ -17,5 +17,5 @@
= form_for [@project, domain_presenter], html: { class: 'fieldset-form' } do |f|
= render 'form', { f: f }
.form-actions.d-flex.justify-content-between
= f.submit _('Save Changes'), class: "btn btn-success"
= link_to _('Cancel'), project_pages_path(@project), class: 'btn btn-default btn-inverse'
= f.submit _('Save Changes'), class: "gl-button btn btn-confirm"
= link_to _('Cancel'), project_pages_path(@project), class: 'gl-button btn btn-default btn-inverse'

View File

@ -6,6 +6,8 @@
- opened_issues_count = issuables_count_for_state(:issues, :opened)
- is_opened_state = params[:state] == 'opened'
- is_closed_state = params[:state] == 'closed'
- issuable_type = 'issues'
- can_edit = can?(current_user, :admin_project, @project)
.row.empty-state
.col-12
@ -45,7 +47,7 @@
= link_to _('New issue'), button_path, class: 'gl-button btn btn-confirm', id: 'new_issue_link'
- if show_import_button
= render 'projects/issues/import_csv/button', type: :text
.js-csv-import-export-buttons{ data: { show_import_button: show_import_button.to_s, issuable_type: issuable_type, import_csv_issues_path: import_csv_namespace_project_issues_path, can_edit: can_edit.to_s, project_import_jira_path: project_import_jira_path(@project), max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes), container_class: 'gl-display-inline-flex gl-vertical-align-middle', show_label: 'true' } }
%hr
%p.gl-text-center.gl-mb-0
%strong
@ -63,6 +65,3 @@
= _("The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project.")
.text-center
= link_to _('Register / Sign In'), new_user_session_path, class: 'gl-button btn btn-confirm'
- if show_import_button
= render 'projects/issues/import_csv/modal'

View File

@ -130,17 +130,8 @@
- if signed_in
.js-sidebar-subscriptions-entry-point
- project_ref = issuable_sidebar[:reference]
.block.with-sub-blocks
.project-reference.sub-block
.sidebar-collapsed-icon.dont-change-state
= clipboard_button(text: project_ref, title: _('Copy reference'), placement: "left", boundary: 'viewport')
.cross-project-reference.hide-collapsed
%span
= _('Reference:')
%cite{ title: project_ref }
= project_ref
= clipboard_button(text: project_ref, title: _('Copy reference'), placement: "left", boundary: 'viewport')
#js-reference-entry-point
- if issuable_type == 'merge_request'
.sidebar-source-branch.sub-block
.sidebar-collapsed-icon.dont-change-state

View File

@ -1,4 +0,0 @@
- if current_user
%button.csv_download_link.btn.gl-button.btn-default.btn-icon.has-tooltip{ title: _('Export as CSV'),
data: { toggle: 'modal', target: ".#{issuable_type}-export-modal", qa_selector: 'export_as_csv_button' } }
= sprite_icon('export')

View File

@ -1,27 +0,0 @@
- class_name = "#{issuable_type.dasherize}-export-modal"
- if current_user
.modal.issuable-export-modal{ class: class_name }
.modal-dialog
.modal-content{ data: { qa_selector: "export_issuable_modal" } }
.modal-header
%h4.gl-m-0
= _("Export %{issuable_type}" % { issuable_type: issuable_type.humanize(capitalize: false) })
%button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
= sprite_icon('close', css_class: 'gl-icon')
.modal-body
- issuable_count = issuables_count_for_state(issuable_type.to_sym, params[:state])
- unless issuable_count == -1 # The count timed out
.modal-subheader
= sprite_icon('check', css_class: 'gl-icon gl-color-green-400')
%strong.gl-ml-3
- if issuable_type.eql?('merge_requests')
= n_("%{count} merge request selected", "%{count} merge requests selected", issuable_count) % { count: issuable_count }
- else
= n_("%{count} issue selected", "%{count} issues selected", issuable_count) % { count: issuable_count }
.modal-text
= html_escape(_('The CSV export will be created in the background. Once finished, it will be sent to %{strong_open}%{email}%{strong_close} in an attachment.')) % { email: @current_user.notification_email, strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
.modal-footer
- if issuable_type.eql?('merge_requests')
= link_to _("Export merge requests"), export_csv_project_merge_requests_path(@project, request.query_parameters), method: :post, class: 'btn gl-button btn-confirm', data: { track_label: "export_merge_requests_csv", track_event: "click_button", track_value: "" }
- else
= link_to _('Export issues'), export_csv_project_issues_path(@project, request.query_parameters), method: :post, class: 'btn gl-button btn-confirm', data: { track_label: "export_issues_csv", track_event: "click_button", track_value: "", qa_selector: "export_issues_button" }

View File

@ -2,7 +2,6 @@
- avatar = true unless local_assigns[:avatar] == false
- use_creator_avatar = false unless local_assigns[:use_creator_avatar] == true
- stars = true unless local_assigns[:stars] == false
- forks = true unless local_assigns[:forks] == false
- pipeline_status = true unless local_assigns[:pipeline_status] == false
- skip_namespace = false unless local_assigns[:skip_namespace] == true
- user = local_assigns[:user]
@ -38,7 +37,7 @@
- css_class = (i >= projects_limit) || project.pending_delete? ? 'hide' : nil
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
avatar: avatar, stars: stars, css_class: css_class, use_creator_avatar: use_creator_avatar,
forks: forks, show_last_commit_as_description: show_last_commit_as_description, user: user,
forks: project.forking_enabled?, show_last_commit_as_description: show_last_commit_as_description, user: user,
merge_requests: project.merge_requests_enabled?, issues: project.issues_enabled?,
pipeline_status: pipeline_status, compact_mode: compact_mode
= paginate_collection(projects, remote: remote) unless skip_pagination

View File

@ -0,0 +1,5 @@
---
title: Fix tooltips failing to hide in commit graph on Firefox
merge_request: 56631
author: Jonathan Duncan
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Notify issue email participants instead of external author
merge_request: 51023
author: Lee Tickett @leetickett
type: other

View File

@ -0,0 +1,5 @@
---
title: Add trigger support for matrix jobs
merge_request: 55348
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Fix upgrade banner for Jira issues showing on group / instance level integrations
merge_request: 56628
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Move to btn-confirm from btn-success in views/import directory
merge_request: 55291
author: Yogi (@yo)
type: changed

View File

@ -0,0 +1,5 @@
---
title: Move from btn-success to btn-confirm in pages_domains directory
merge_request: 56349
author: Yogi (@yo)
type: changed

View File

@ -0,0 +1,5 @@
---
title: Move from btn-success to btn-confirm in pages directory
merge_request: 56348
author: Yogi (@yo)
type: changed

View File

@ -0,0 +1,5 @@
---
title: Hide fork count and link in project list where forks are disabled
merge_request: 56520
author: Simon Stieger @sim0
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Remove bottom border from header
merge_request: 56315
author:
type: other

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
status: data_available
status: deprecated
time_frame: 28d
data_source:
distribution:

View File

@ -7,7 +7,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -7,7 +7,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -6,7 +6,7 @@ product_stage: manage
product_group: group::import
product_category:
value_type: number
status: data_available
status: deprecated
time_frame: all
data_source:
distribution:

View File

@ -175,7 +175,7 @@ successfully, you must replicate their data using some other means.
| [Application data in PostgreSQL](../../postgresql/index.md) | **Yes** (10.2) | **Yes** (10.2) | No | |
| [Project repository](../../../user/project/repository/) | **Yes** (10.2) | **Yes** (10.7) | No | |
| [Project wiki repository](../../../user/project/wiki/) | **Yes** (10.2) | **Yes** (10.7) | No |
| [Group wiki repository](../../../user/group/index.md#group-wikis) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | No | |
| [Group wiki repository](../../../user/group/index.md#group-wikis) | [**Yes** (13.10)](https://gitlab.com/gitlab-org/gitlab/-/issues/208147) | No | No | Behind feature flag `geo_group_wiki_repository_replication`, enabled by default |
| [Uploads](../../uploads.md) | **Yes** (10.2) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1817) | No | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them. |
| [LFS objects](../../lfs/index.md) | **Yes** (10.2) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/8922) | Via Object Storage provider if supported. Native Geo support (Beta). | Verified only on transfer or manually using [Integrity Check Rake Task](../../raketasks/check.md) on both nodes and comparing the output between them. GitLab versions 11.11.x and 12.0.x are affected by [a bug that prevents any new LFS objects from replicating](https://gitlab.com/gitlab-org/gitlab/-/issues/32696). |
| [Personal snippets](../../../user/snippets.md) | **Yes** (10.2) | **Yes** (10.2) | No | |

View File

@ -4,7 +4,7 @@ group: Conversion
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Instance Review
# Instance Review **(FREE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6995) in [GitLab Free](https://about.gitlab.com/pricing/) 11.3.

View File

@ -190,6 +190,9 @@ then you'll need to also add the full paths as shown below:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
WARNING:
Multiple wildcards for one instance is not supported. Only one wildcard per instance can be assigned.
### Additional configuration for Docker container
The GitLab Pages daemon doesn't have permissions to bind mounts when it runs
@ -749,6 +752,8 @@ The default value for Omnibus installations is `nil`.
If left unchanged, GitLab Pages tries to use any available source (either `gitlab` or `disk`). The
preferred source is `gitlab`, which uses [API-based configuration](#gitlab-api-based-configuration).
On large GitLab instances, using the API-based configuration will significantly improve the pages daemon startup time, as there is no need to load all custom domains configuration into memory.
For more details see this [blog post](https://about.gitlab.com/blog/2020/08/03/how-gitlab-pages-uses-the-gitlab-api-to-serve-content/).
### Deprecated `domain_config_source`

View File

@ -5425,6 +5425,34 @@ An edge in a connection.
| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
| `node` | [`SastCiConfigurationOptionsEntity`](#sastciconfigurationoptionsentity) | The item at the end of the edge. |
### `Scan`
Represents the security scan information.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `errors` | [`[String!]!`](#string) | List of errors. |
| `name` | [`String!`](#string) | Name of the scan. |
### `ScanConnection`
The connection type for Scan.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `edges` | [`[ScanEdge]`](#scanedge) | A list of edges. |
| `nodes` | [`[Scan]`](#scan) | A list of nodes. |
| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
### `ScanEdge`
An edge in a connection.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
| `node` | [`Scan`](#scan) | The item at the end of the edge. |
### `ScannedResource`
Represents a resource scanned by a security scan.
@ -5476,6 +5504,7 @@ Represents a section of a summary of a security report.
| `scannedResources` | [`ScannedResourceConnection`](#scannedresourceconnection) | A list of the first 20 scanned resources. |
| `scannedResourcesCount` | [`Int`](#int) | Total number of scanned resources. |
| `scannedResourcesCsvPath` | [`String`](#string) | Path to download all the scanned resources in CSV format. |
| `scans` | [`ScanConnection!`](#scanconnection) | List of security scans ran for the type. |
| `vulnerabilitiesCount` | [`Int`](#int) | Total number of vulnerabilities. |
### `SecurityScanners`

View File

@ -3662,6 +3662,40 @@ deploystacks:
- PROVIDER: [aws, ovh, gcp, vultr]
```
##### Parallel `matrix` trigger jobs
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/270957) in GitLab 13.10.
Use `matrix:` to run a [trigger](#trigger) job multiple times in parallel in a single pipeline,
but with different variable values for each instance of the job.
```yaml
deploystacks:
stage: deploy
trigger:
include: path/to/child-pipeline.yml
parallel:
matrix:
- PROVIDER: aws
STACK: [monitoring, app1]
- PROVIDER: ovh
STACK: [monitoring, backup]
- PROVIDER: [gcp, vultr]
STACK: [data]
```
This example generates 6 parallel `deploystacks` trigger jobs, each with different values
for `PROVIDER` and `STACK`, and they create 6 different child pipelines with those variables.
```plaintext
deploystacks: [aws, monitoring]
deploystacks: [aws, app1]
deploystacks: [ovh, monitoring]
deploystacks: [ovh, backup]
deploystacks: [gcp, data]
deploystacks: [vultr, data]
```
### `trigger`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8997) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8.

View File

@ -673,10 +673,10 @@ that are scoped to a single [configuration keyword](../ci/yaml/README.md#job-key
| `.qa-cache` | Allows a job to use a default `cache` definition suitable for QA tasks. |
| `.yarn-cache` | Allows a job to use a default `cache` definition suitable for frontend jobs that do a `yarn install`. |
| `.assets-compile-cache` | Allows a job to use a default `cache` definition suitable for frontend jobs that compile assets. |
| `.use-pg11` | Allows a job to use the `postgres:11.6` and `redis:4.0-alpine` services. |
| `.use-pg11-ee` | Same as `.use-pg11` but also use the `docker.elastic.co/elasticsearch/elasticsearch:7.9.2` services. |
| `.use-pg12` | Allows a job to use the `postgres:12` and `redis:4.0-alpine` services. |
| `.use-pg12-ee` | Same as `.use-pg12` but also use the `docker.elastic.co/elasticsearch/elasticsearch:7.9.2` services. |
| `.use-pg11` | Allows a job to run the `postgres` 11 and `redis` services (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific versions of the services). |
| `.use-pg11-ee` | Same as `.use-pg11` but also use an `elasticsearch` service (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific version of the service). |
| `.use-pg12` | Allows a job to use the `postgres` 12 and `redis` services (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific versions of the services). |
| `.use-pg12-ee` | Same as `.use-pg12` but also use an `elasticsearch` service (see [`.gitlab/ci/global.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab/ci/global.gitlab-ci.yml) for the specific version of the service). |
| `.use-kaniko` | Allows a job to use the `kaniko` tool to build Docker images. |
| `.as-if-foss` | Simulate the FOSS project by setting the `FOSS_ONLY='1'` CI/CD variable. |
| `.use-docker-in-docker` | Allows a job to use Docker in Docker. |

Some files were not shown because too many files have changed in this diff Show More