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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-14 18:09:57 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-14 18:09:57 +0300
commitb689f371350fbf1b71f266764ee018befc9b91f7 (patch)
tree7de1d3ab26d3cae0ac2a7a8ccd8302fcdaac5534 /app
parent0b194c4854f312e36616fccf7c610cb2b0ec6957 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue36
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js18
-rw-r--r--app/assets/javascripts/branches/components/delete_branch_button.vue8
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_image_button.vue110
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_link_button.vue4
-rw-r--r--app/assets/javascripts/content_editor/components/top_toolbar.vue13
-rw-r--r--app/assets/javascripts/content_editor/extensions/link.js6
-rw-r--r--app/assets/javascripts/import_entities/components/group_dropdown.vue40
-rw-r--r--app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue26
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue14
-rw-r--r--app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue62
-rw-r--r--app/assets/javascripts/import_entities/import_projects/index.js2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue68
-rw-r--r--app/assets/javascripts/repository/components/tree_content.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/select2_select.vue48
-rw-r--r--app/assets/stylesheets/snippets.scss15
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/models/integrations/datadog.rb34
-rw-r--r--app/services/ci/register_job_service.rb2
-rw-r--r--app/services/service_ping/submit_service.rb42
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml4
21 files changed, 400 insertions, 160 deletions
diff --git a/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue b/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue
index d58033b36c7..a490111e13b 100644
--- a/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue
+++ b/app/assets/javascripts/analytics/shared/components/projects_dropdown_filter.vue
@@ -62,6 +62,7 @@ export default {
projects: [],
selectedProjects: this.defaultProjects || [],
searchTerm: '',
+ isDirty: false,
};
},
computed: {
@@ -124,6 +125,24 @@ export default {
this.setSelectedProjects(project, !isSelected);
this.$emit('selected', this.selectedProjects);
},
+ onMultiSelectClick({ project, isSelected }) {
+ this.setSelectedProjects(project, !isSelected);
+ this.isDirty = true;
+ },
+ onSelected(ev) {
+ if (this.multiSelect) {
+ this.onMultiSelectClick(ev);
+ } else {
+ this.onClick(ev);
+ }
+ },
+ onHide() {
+ if (this.multiSelect && this.isDirty) {
+ this.$emit('selected', this.selectedProjects);
+ }
+ this.searchTerm = '';
+ this.isDirty = false;
+ },
fetchData() {
this.loading = true;
@@ -158,12 +177,12 @@ export default {
},
};
</script>
-
<template>
<gl-dropdown
ref="projectsDropdown"
class="dropdown dropdown-projects"
toggle-class="gl-shadow-none"
+ @hide="onHide"
>
<template #button-content>
<div class="gl-display-flex gl-flex-grow-1">
@@ -181,15 +200,18 @@ export default {
</div>
<gl-icon class="gl-ml-2" name="chevron-down" />
</template>
- <gl-dropdown-section-header>{{ __('Projects') }}</gl-dropdown-section-header>
- <gl-search-box-by-type v-model.trim="searchTerm" />
-
+ <template #header>
+ <gl-dropdown-section-header>{{ __('Projects') }}</gl-dropdown-section-header>
+ <gl-search-box-by-type v-model.trim="searchTerm" />
+ </template>
<gl-dropdown-item
v-for="project in availableProjects"
:key="project.id"
:is-check-item="true"
:is-checked="isProjectSelected(project.id)"
- @click.prevent="onClick({ project, isSelected: isProjectSelected(project.id) })"
+ @click.native.capture.stop="
+ onSelected({ project, isSelected: isProjectSelected(project.id) })
+ "
>
<div class="gl-display-flex">
<gl-avatar
@@ -203,7 +225,9 @@ export default {
/>
<div>
<div data-testid="project-name">{{ project.name }}</div>
- <div class="gl-text-gray-500" data-testid="project-full-path">{{ project.fullPath }}</div>
+ <div class="gl-text-gray-500" data-testid="project-full-path">
+ {{ project.fullPath }}
+ </div>
</div>
</div>
</gl-dropdown-item>
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 6cd0a62657e..a32a100fa11 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -35,13 +35,23 @@ export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId
export default {
[mutationTypes.SET_INITIAL_BOARD_DATA](state, data) {
- const { boardType, disabled, boardId, fullBoardId, fullPath, boardConfig, issuableType } = data;
+ const {
+ allowSubEpics,
+ boardConfig,
+ boardId,
+ boardType,
+ disabled,
+ fullBoardId,
+ fullPath,
+ issuableType,
+ } = data;
+ state.allowSubEpics = allowSubEpics;
+ state.boardConfig = boardConfig;
state.boardId = boardId;
- state.fullBoardId = fullBoardId;
- state.fullPath = fullPath;
state.boardType = boardType;
state.disabled = disabled;
- state.boardConfig = boardConfig;
+ state.fullBoardId = fullBoardId;
+ state.fullPath = fullPath;
state.issuableType = issuableType;
},
diff --git a/app/assets/javascripts/branches/components/delete_branch_button.vue b/app/assets/javascripts/branches/components/delete_branch_button.vue
index 5a5f49e25e7..6a6d4d48c52 100644
--- a/app/assets/javascripts/branches/components/delete_branch_button.vue
+++ b/app/assets/javascripts/branches/components/delete_branch_button.vue
@@ -47,12 +47,6 @@ export default {
},
},
computed: {
- variant() {
- if (this.disabled) {
- return 'default';
- }
- return 'danger';
- },
title() {
if (this.isProtectedBranch && this.disabled) {
return s__('Branches|Only a project maintainer or owner can delete a protected branch');
@@ -83,7 +77,7 @@ export default {
class="js-delete-branch-button"
data-qa-selector="delete_branch_button"
:disabled="disabled"
- :variant="variant"
+ variant="default"
:title="title"
:aria-label="title"
@click="openModal"
diff --git a/app/assets/javascripts/content_editor/components/toolbar_image_button.vue b/app/assets/javascripts/content_editor/components/toolbar_image_button.vue
new file mode 100644
index 00000000000..ebeee16dbec
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/toolbar_image_button.vue
@@ -0,0 +1,110 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownForm,
+ GlButton,
+ GlFormInputGroup,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlTooltipDirective as GlTooltip,
+} from '@gitlab/ui';
+import { Editor as TiptapEditor } from '@tiptap/vue-2';
+import { acceptedMimes } from '../extensions/image';
+import { getImageAlt } from '../services/utils';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownForm,
+ GlFormInputGroup,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlButton,
+ },
+ directives: {
+ GlTooltip,
+ },
+ props: {
+ tiptapEditor: {
+ type: TiptapEditor,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ imgSrc: '',
+ };
+ },
+ methods: {
+ resetFields() {
+ this.imgSrc = '';
+ this.$refs.fileSelector.value = '';
+ },
+ insertImage() {
+ this.tiptapEditor
+ .chain()
+ .focus()
+ .setImage({
+ src: this.imgSrc,
+ canonicalSrc: this.imgSrc,
+ alt: getImageAlt(this.imgSrc),
+ })
+ .run();
+
+ this.resetFields();
+ this.emitExecute();
+ },
+ emitExecute(source = 'url') {
+ this.$emit('execute', { contentType: 'image', value: source });
+ },
+ openFileUpload() {
+ this.$refs.fileSelector.click();
+ },
+ onFileSelect(e) {
+ this.tiptapEditor
+ .chain()
+ .focus()
+ .uploadImage({
+ file: e.target.files[0],
+ })
+ .run();
+
+ this.resetFields();
+ this.emitExecute('upload');
+ },
+ },
+ acceptedMimes,
+};
+</script>
+<template>
+ <gl-dropdown
+ v-gl-tooltip
+ :aria-label="__('Insert image')"
+ :title="__('Insert image')"
+ size="small"
+ category="tertiary"
+ icon="media"
+ @hidden="resetFields()"
+ >
+ <gl-dropdown-form class="gl-px-3!">
+ <gl-form-input-group v-model="imgSrc" :placeholder="__('Image URL')">
+ <template #append>
+ <gl-button variant="confirm" @click="insertImage">{{ __('Insert') }}</gl-button>
+ </template>
+ </gl-form-input-group>
+ </gl-dropdown-form>
+ <gl-dropdown-divider />
+ <gl-dropdown-item @click="openFileUpload">
+ {{ __('Upload image') }}
+ </gl-dropdown-item>
+
+ <input
+ ref="fileSelector"
+ type="file"
+ name="content_editor_image"
+ :accept="$options.acceptedMimes"
+ class="gl-display-none"
+ @change="onFileSelect"
+ />
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/content_editor/components/toolbar_link_button.vue b/app/assets/javascripts/content_editor/components/toolbar_link_button.vue
index c0ced2d2228..8f57959a73f 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_link_button.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_link_button.vue
@@ -43,7 +43,7 @@ export default {
},
mounted() {
this.tiptapEditor.on('selectionUpdate', ({ editor }) => {
- const { 'data-canonical-src': canonicalSrc, href } = editor.getAttributes(linkContentType);
+ const { canonicalSrc, href } = editor.getAttributes(linkContentType);
this.linkHref = canonicalSrc || href;
});
@@ -56,7 +56,7 @@ export default {
.unsetLink()
.setLink({
href: this.linkHref,
- 'data-canonical-src': this.linkHref,
+ canonicalSrc: this.linkHref,
})
.run();
diff --git a/app/assets/javascripts/content_editor/components/top_toolbar.vue b/app/assets/javascripts/content_editor/components/top_toolbar.vue
index e1bc26d50fc..fafc7a660e7 100644
--- a/app/assets/javascripts/content_editor/components/top_toolbar.vue
+++ b/app/assets/javascripts/content_editor/components/top_toolbar.vue
@@ -4,6 +4,7 @@ import { CONTENT_EDITOR_TRACKING_LABEL, TOOLBAR_CONTROL_TRACKING_ACTION } from '
import { ContentEditor } from '../services/content_editor';
import Divider from './divider.vue';
import ToolbarButton from './toolbar_button.vue';
+import ToolbarImageButton from './toolbar_image_button.vue';
import ToolbarLinkButton from './toolbar_link_button.vue';
import ToolbarTableButton from './toolbar_table_button.vue';
import ToolbarTextStyleDropdown from './toolbar_text_style_dropdown.vue';
@@ -18,6 +19,7 @@ export default {
ToolbarTextStyleDropdown,
ToolbarLinkButton,
ToolbarTableButton,
+ ToolbarImageButton,
Divider,
},
mixins: [trackingMixin],
@@ -89,6 +91,12 @@ export default {
@execute="trackToolbarControlExecution"
/>
<divider />
+ <toolbar-image-button
+ ref="imageButton"
+ data-testid="image"
+ :tiptap-editor="contentEditor.tiptapEditor"
+ @execute="trackToolbarControlExecution"
+ />
<toolbar-button
data-testid="blockquote"
content-type="blockquote"
@@ -140,3 +148,8 @@ export default {
/>
</div>
</template>
+<style>
+.gl-spinner-container {
+ text-align: left;
+}
+</style>
diff --git a/app/assets/javascripts/content_editor/extensions/link.js b/app/assets/javascripts/content_editor/extensions/link.js
index 89076e0210c..12019ab4636 100644
--- a/app/assets/javascripts/content_editor/extensions/link.js
+++ b/app/assets/javascripts/content_editor/extensions/link.js
@@ -38,11 +38,11 @@ export const tiptapExtension = Link.extend({
};
},
},
- 'data-canonical-src': {
+ canonicalSrc: {
default: null,
parseHTML: (element) => {
return {
- href: element.dataset.canonicalSrc,
+ canonicalSrc: element.dataset.canonicalSrc,
};
},
},
@@ -57,7 +57,7 @@ export const serializer = {
return '[';
},
close(state, mark) {
- const href = mark.attrs['data-canonical-src'] || mark.attrs.href;
+ const href = mark.attrs.canonicalSrc || mark.attrs.href;
return `](${state.esc(href)}${mark.attrs.title ? ` ${state.quote(mark.attrs.title)}` : ''})`;
},
};
diff --git a/app/assets/javascripts/import_entities/components/group_dropdown.vue b/app/assets/javascripts/import_entities/components/group_dropdown.vue
new file mode 100644
index 00000000000..44d6d17232f
--- /dev/null
+++ b/app/assets/javascripts/import_entities/components/group_dropdown.vue
@@ -0,0 +1,40 @@
+<script>
+import { GlDropdown, GlSearchBoxByType } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlDropdown,
+ GlSearchBoxByType,
+ },
+ inheritAttrs: false,
+ props: {
+ namespaces: {
+ type: Array,
+ required: true,
+ },
+ },
+ data() {
+ return { searchTerm: '' };
+ },
+ computed: {
+ filteredNamespaces() {
+ return this.namespaces.filter((ns) =>
+ ns.toLowerCase().includes(this.searchTerm.toLowerCase()),
+ );
+ },
+ },
+};
+</script>
+<template>
+ <gl-dropdown
+ toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
+ class="import-entities-namespace-dropdown gl-h-7 gl-flex-fill-1"
+ data-qa-selector="target_namespace_selector_dropdown"
+ v-bind="$attrs"
+ >
+ <template #header>
+ <gl-search-box-by-type v-model.trim="searchTerm" />
+ </template>
+ <slot :namespaces="filteredNamespaces"></slot>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue b/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
index 63c18f4d78e..1c3ede769e0 100644
--- a/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
+++ b/app/assets/javascripts/import_entities/import_groups/components/import_table_row.vue
@@ -1,7 +1,6 @@
<script>
import {
GlButton,
- GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
@@ -11,6 +10,7 @@ import {
} from '@gitlab/ui';
import { joinPaths } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
+import ImportGroupDropdown from '../../components/group_dropdown.vue';
import ImportStatus from '../../components/import_status.vue';
import { STATUSES } from '../../constants';
import addValidationErrorMutation from '../graphql/mutations/add_validation_error.mutation.graphql';
@@ -22,8 +22,8 @@ const DEBOUNCE_INTERVAL = 300;
export default {
components: {
ImportStatus,
+ ImportGroupDropdown,
GlButton,
- GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlDropdownSectionHeader,
@@ -83,6 +83,10 @@ export default {
},
computed: {
+ availableNamespaceNames() {
+ return this.availableNamespaces.map((ns) => ns.full_path);
+ },
+
importTarget() {
return this.group.import_target;
},
@@ -153,9 +157,11 @@ export default {
disabled: isAlreadyImported,
}"
>
- <gl-dropdown
+ <import-group-dropdown
+ #default="{ namespaces }"
:text="importTarget.target_namespace"
:disabled="isAlreadyImported"
+ :namespaces="availableNamespaceNames"
toggle-class="gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
class="import-entities-namespace-dropdown gl-h-7 gl-flex-grow-1"
data-qa-selector="target_namespace_selector_dropdown"
@@ -163,22 +169,22 @@ export default {
<gl-dropdown-item @click="$emit('update-target-namespace', '')">{{
s__('BulkImport|No parent')
}}</gl-dropdown-item>
- <template v-if="availableNamespaces.length">
+ <template v-if="namespaces.length">
<gl-dropdown-divider />
<gl-dropdown-section-header>
{{ s__('BulkImport|Existing groups') }}
</gl-dropdown-section-header>
<gl-dropdown-item
- v-for="ns in availableNamespaces"
- :key="ns.full_path"
+ v-for="ns in namespaces"
+ :key="ns"
data-qa-selector="target_group_dropdown_item"
- :data-qa-group-name="ns.full_path"
- @click="$emit('update-target-namespace', ns.full_path)"
+ :data-qa-group-name="ns"
+ @click="$emit('update-target-namespace', ns)"
>
- {{ ns.full_path }}
+ {{ ns }}
</gl-dropdown-item>
</template>
- </gl-dropdown>
+ </import-group-dropdown>
<div
class="import-entities-target-select-separator gl-h-7 gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1"
>
diff --git a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
index be09052fb7e..14d08caef34 100644
--- a/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
+++ b/app/assets/javascripts/import_entities/import_projects/components/import_projects_table.vue
@@ -47,18 +47,7 @@ export default {
},
availableNamespaces() {
- const serializedNamespaces = this.namespaces.map(({ fullPath }) => ({
- id: fullPath,
- text: fullPath,
- }));
-
- return [
- { text: __('Groups'), children: serializedNamespaces },
- {
- text: __('Users'),
- children: [{ id: this.defaultTargetNamespace, text: this.defaultTargetNamespace }],
- },
- ];
+ return this.namespaces.map(({ fullPath }) => fullPath);
},
importAllButtonText() {
@@ -179,6 +168,7 @@ export default {
:key="repo.importSource.providerLink"
:repo="repo"
:available-namespaces="availableNamespaces"
+ :user-namespace="defaultTargetNamespace"
/>
</template>
</tbody>
diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
index a803afeb901..e2fd608d9db 100644
--- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
+++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue
@@ -1,8 +1,17 @@
<script>
-import { GlIcon, GlBadge, GlFormInput, GlButton, GlLink } from '@gitlab/ui';
+import {
+ GlIcon,
+ GlBadge,
+ GlFormInput,
+ GlButton,
+ GlLink,
+ GlDropdownItem,
+ GlDropdownDivider,
+ GlDropdownSectionHeader,
+} from '@gitlab/ui';
import { mapState, mapGetters, mapActions } from 'vuex';
import { __ } from '~/locale';
-import Select2Select from '~/vue_shared/components/select2_select.vue';
+import ImportGroupDropdown from '../../components/group_dropdown.vue';
import ImportStatus from '../../components/import_status.vue';
import { STATUSES } from '../../constants';
import { isProjectImportable, isIncompatible, getImportStatus } from '../utils';
@@ -10,10 +19,13 @@ import { isProjectImportable, isIncompatible, getImportStatus } from '../utils';
export default {
name: 'ProviderRepoTableRow',
components: {
- Select2Select,
+ ImportGroupDropdown,
ImportStatus,
GlFormInput,
GlButton,
+ GlDropdownItem,
+ GlDropdownDivider,
+ GlDropdownSectionHeader,
GlIcon,
GlBadge,
GlLink,
@@ -23,6 +35,10 @@ export default {
type: Object,
required: true,
},
+ userNamespace: {
+ type: String,
+ required: true,
+ },
availableNamespaces: {
type: Array,
required: true,
@@ -61,22 +77,6 @@ export default {
return this.ciCdOnly ? __('Connect') : __('Import');
},
- select2Options() {
- return {
- data: this.availableNamespaces,
- containerCssClass: 'import-namespace-select qa-project-namespace-select gl-w-auto',
- };
- },
-
- targetNamespaceSelect: {
- get() {
- return this.importTarget.targetNamespace;
- },
- set(value) {
- this.updateImportTarget({ targetNamespace: value });
- },
- },
-
newNameInput: {
get() {
return this.importTarget.newName;
@@ -118,7 +118,29 @@ export default {
<template v-if="repo.importSource.target">{{ repo.importSource.target }}</template>
<template v-else-if="isImportNotStarted">
<div class="import-entities-target-select gl-display-flex gl-align-items-stretch gl-w-full">
- <select2-select v-model="targetNamespaceSelect" :options="select2Options" />
+ <import-group-dropdown
+ #default="{ namespaces }"
+ :text="importTarget.targetNamespace"
+ :namespaces="availableNamespaces"
+ >
+ <template v-if="namespaces.length">
+ <gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="ns in namespaces"
+ :key="ns"
+ data-qa-selector="target_group_dropdown_item"
+ :data-qa-group-name="ns"
+ @click="updateImportTarget({ targetNamespace: ns })"
+ >
+ {{ ns }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider />
+ </template>
+ <gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header>
+ <gl-dropdown-item @click="updateImportTarget({ targetNamespace: ns })">{{
+ userNamespace
+ }}</gl-dropdown-item>
+ </import-group-dropdown>
<div
class="import-entities-target-select-separator gl-px-3 gl-display-flex gl-align-items-center gl-border-solid gl-border-0 gl-border-t-1 gl-border-b-1"
>
diff --git a/app/assets/javascripts/import_entities/import_projects/index.js b/app/assets/javascripts/import_entities/import_projects/index.js
index 6b7fe23ed60..110cc77b20d 100644
--- a/app/assets/javascripts/import_entities/import_projects/index.js
+++ b/app/assets/javascripts/import_entities/import_projects/index.js
@@ -38,7 +38,7 @@ export function initStoreFromElement(element) {
export function initPropsFromElement(element) {
return {
- providerTitle: element.dataset.providerTitle,
+ providerTitle: element.dataset.provider,
filterable: parseBoolean(element.dataset.filterable),
paginatable: parseBoolean(element.dataset.paginatable),
};
diff --git a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue b/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
index 853e839a7ab..73c91839f04 100644
--- a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
+++ b/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue
@@ -1,18 +1,27 @@
<script>
-import { GlIcon } from '@gitlab/ui';
+import { GlAlert, GlLink, GlSprintf, GlIcon } from '@gitlab/ui';
import { uniqueId } from 'lodash';
+import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
import SourceEditor from '~/vue_shared/components/source_editor.vue';
+import getCurrentBranch from '../../graphql/queries/client/current_branch.graphql';
export default {
i18n: {
viewOnlyMessage: s__('Pipelines|Merged YAML is view only'),
+ unavailableDefaultTitle: s__('Pipelines|Merged YAML unavailable'),
+ unavailableDefaultText: s__(
+ 'Pipelines|The merged YAML view is only available for the default branch. %{linkStart}Learn more.%{linkEnd}',
+ ),
},
components: {
SourceEditor,
+ GlAlert,
GlIcon,
+ GlLink,
+ GlSprintf,
},
- inject: ['ciConfigPath'],
+ inject: ['ciConfigPath', 'defaultBranch'],
props: {
ciConfigData: {
type: Object,
@@ -24,6 +33,15 @@ export default {
failureType: null,
};
},
+ // This is not the best practice, don't copy me (@samdbeckham)
+ // This is a temporary workaround to unblock a release.
+ // See this comment for more information on this approach
+ // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65972#note_626095648
+ apollo: {
+ currentBranch: {
+ query: getCurrentBranch,
+ },
+ },
computed: {
fileGlobalId() {
return `${this.ciConfigPath}-${uniqueId()}`;
@@ -31,24 +49,44 @@ export default {
mergedYaml() {
return this.ciConfigData.mergedYaml;
},
+ isOnDefaultBranch() {
+ return this.currentBranch === this.defaultBranch;
+ },
+ expandedConfigHelpPath() {
+ return helpPagePath('ci/pipeline_editor/index', { anchor: 'view-expanded-configuration' });
+ },
},
};
</script>
<template>
<div>
- <div class="gl-display-flex gl-align-items-center">
- <gl-icon :size="16" name="lock" class="gl-text-gray-500 gl-mr-3" />
- {{ $options.i18n.viewOnlyMessage }}
- </div>
- <div class="gl-mt-3 gl-border-solid gl-border-gray-100 gl-border-1">
- <source-editor
- ref="editor"
- :value="mergedYaml"
- :file-name="ciConfigPath"
- :file-global-id="fileGlobalId"
- :editor-options="{ readOnly: true }"
- v-on="$listeners"
- />
+ <div v-if="isOnDefaultBranch">
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-icon :size="16" name="lock" class="gl-text-gray-500 gl-mr-3" />
+ {{ $options.i18n.viewOnlyMessage }}
+ </div>
+ <div class="gl-mt-3 gl-border-solid gl-border-gray-100 gl-border-1">
+ <source-editor
+ ref="editor"
+ :value="mergedYaml"
+ :file-name="ciConfigPath"
+ :file-global-id="fileGlobalId"
+ :editor-options="{ readOnly: true }"
+ v-on="$listeners"
+ />
+ </div>
</div>
+ <gl-alert
+ v-else
+ variant="info"
+ :dismissible="false"
+ :title="$options.i18n.unavailableDefaultTitle"
+ >
+ <gl-sprintf :message="$options.i18n.unavailableDefaultText">
+ <template #link="{ content }">
+ <gl-link :href="expandedConfigHelpPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-alert>
</div>
</template>
diff --git a/app/assets/javascripts/repository/components/tree_content.vue b/app/assets/javascripts/repository/components/tree_content.vue
index cf1cff9023e..c861fb8dd06 100644
--- a/app/assets/javascripts/repository/components/tree_content.vue
+++ b/app/assets/javascripts/repository/components/tree_content.vue
@@ -1,6 +1,7 @@
<script>
import filesQuery from 'shared_queries/repository/files.query.graphql';
import createFlash from '~/flash';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __ } from '../../locale';
import { TREE_PAGE_SIZE, TREE_INITIAL_FETCH_COUNT, TREE_PAGE_LIMIT } from '../constants';
import getRefMixin from '../mixins/get_ref';
@@ -14,7 +15,7 @@ export default {
FileTable,
FilePreview,
},
- mixins: [getRefMixin],
+ mixins: [getRefMixin, glFeatureFlagMixin()],
apollo: {
projectPath: {
query: projectPathQuery,
@@ -52,7 +53,9 @@ export default {
pageSize() {
// we want to exponentially increase the page size to reduce the load on the frontend
const exponentialSize = (TREE_PAGE_SIZE / TREE_INITIAL_FETCH_COUNT) * (this.fetchCounter + 1);
- return exponentialSize < TREE_PAGE_SIZE ? exponentialSize : TREE_PAGE_SIZE;
+ return exponentialSize < TREE_PAGE_SIZE && this.glFeatures.increasePageSizeExponentially
+ ? exponentialSize
+ : TREE_PAGE_SIZE;
},
totalEntries() {
return Object.values(this.entries).flat().length;
diff --git a/app/assets/javascripts/vue_shared/components/select2_select.vue b/app/assets/javascripts/vue_shared/components/select2_select.vue
deleted file mode 100644
index bb1a8fae7b0..00000000000
--- a/app/assets/javascripts/vue_shared/components/select2_select.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
-import $ from 'jquery';
-import 'select2';
-import { loadCSSFile } from '~/lib/utils/css_utils';
-
-export default {
- // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
- // eslint-disable-next-line @gitlab/require-i18n-strings
- name: 'Select2Select',
- props: {
- options: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- value: {
- type: String,
- required: false,
- default: '',
- },
- },
-
- watch: {
- value() {
- $(this.$refs.dropdownInput).val(this.value).trigger('change');
- },
- },
-
- mounted() {
- loadCSSFile(gon.select2_css_path)
- .then(() => {
- $(this.$refs.dropdownInput)
- .val(this.value)
- .select2(this.options)
- .on('change', (event) => this.$emit('input', event.target.value));
- })
- .catch(() => {});
- },
-
- beforeDestroy() {
- $(this.$refs.dropdownInput).select2('destroy');
- },
-};
-</script>
-
-<template>
- <input ref="dropdownInput" type="hidden" />
-</template>
diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss
index ad040f65f3c..d38c1818f53 100644
--- a/app/assets/stylesheets/snippets.scss
+++ b/app/assets/stylesheets/snippets.scss
@@ -54,6 +54,8 @@
white-space: pre;
word-wrap: normal;
border-left: $border-style;
+ text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%; /* stylelint-disable-line property-no-vendor-prefix */
}
code {
@@ -65,7 +67,7 @@
}
.line-numbers {
- padding: 10px;
+ padding: 10px 10px 10px 0;
text-align: right;
float: left;
@@ -86,18 +88,24 @@
}
}
+ .file-actions {
+ flex-shrink: 0;
+ }
+
.file-title-flex-parent {
display: flex;
- align-items: center;
+ align-items: flex-start;
justify-content: space-between;
background-color: $gray-light;
border: $border-style;
border-bottom: 0;
- padding: $gl-padding-top $gl-padding;
+ padding: $gl-padding;
margin: 0;
border-radius: $border-radius-default $border-radius-default 0 0;
.file-header-content {
+ max-width: 75%;
+
.file-title-name {
font-weight: $gl-font-weight-bold;
}
@@ -105,6 +113,7 @@
.gitlab-embedded-snippets-title {
text-decoration: none;
color: $gl-text-color;
+ word-break: break-word;
&:hover {
text-decoration: underline;
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 48d943587d4..0dbf7d40f87 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -37,6 +37,7 @@ class ProjectsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml)
+ push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml)
end
layout :determine_layout
diff --git a/app/models/integrations/datadog.rb b/app/models/integrations/datadog.rb
index 211c80f8e33..d7d472f1d89 100644
--- a/app/models/integrations/datadog.rb
+++ b/app/models/integrations/datadog.rb
@@ -2,10 +2,10 @@
module Integrations
class Datadog < Integration
- DEFAULT_SITE = 'datadoghq.com'
- URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_site}/v1/input/'
- URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_site}/account/settings#api'
- URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_SITE}/account_management/api-app-keys/"
+ DEFAULT_DOMAIN = 'datadoghq.com'
+ URL_TEMPLATE = 'https://webhooks-http-intake.logs.%{datadog_domain}/api/v2/webhook'
+ URL_TEMPLATE_API_KEYS = 'https://app.%{datadog_domain}/account/settings#api'
+ URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_DOMAIN}/account_management/api-app-keys/"
SUPPORTED_EVENTS = %w[
pipeline job
@@ -26,7 +26,7 @@ module Integrations
def initialize_properties
super
- self.datadog_site ||= DEFAULT_SITE
+ self.datadog_site ||= DEFAULT_DOMAIN
end
def self.supported_events
@@ -62,7 +62,7 @@ module Integrations
{
type: 'text',
name: 'datadog_site',
- placeholder: DEFAULT_SITE,
+ placeholder: DEFAULT_DOMAIN,
help: 'Choose the Datadog site to send data to. Set to "datadoghq.eu" to send data to the EU site',
required: false
},
@@ -105,18 +105,21 @@ module Integrations
end
def hook_url
- url = api_url.presence || sprintf(URL_TEMPLATE, datadog_site: datadog_site)
+ url = api_url.presence || sprintf(URL_TEMPLATE, datadog_domain: datadog_domain)
url = URI.parse(url)
- url.path = File.join(url.path || '/', api_key)
- query = { service: datadog_service.presence, env: datadog_env.presence }.compact
- url.query = query.to_query unless query.empty?
+ query = {
+ "dd-api-key" => api_key,
+ service: datadog_service.presence,
+ env: datadog_env.presence
+ }.compact
+ url.query = query.to_query
url.to_s
end
def api_keys_url
return URL_API_KEYS_DOCS unless datadog_site.presence
- sprintf(URL_TEMPLATE_API_KEYS, datadog_site: datadog_site)
+ sprintf(URL_TEMPLATE_API_KEYS, datadog_domain: datadog_domain)
end
def execute(data)
@@ -137,5 +140,14 @@ module Integrations
{ success: true, result: result[:message] }
end
+
+ private
+
+ def datadog_domain
+ # Transparently ignore "app" prefix from datadog_site as the official docs table in
+ # https://docs.datadoghq.com/getting_started/site/ is confusing for internal URLs.
+ # US3 needs to keep a prefix but other datacenters cannot have the listed "app" prefix
+ datadog_site.delete_prefix("app.")
+ end
end
end
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index ec50312c6d4..dc046e1d164 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -209,7 +209,7 @@ module Ci
# We need to use the presenter here because Gitaly calls in the presenter
# may fail, and we need to ensure the response has been generated.
presented_build = ::Ci::BuildRunnerPresenter.new(build) # rubocop:disable CodeReuse/Presenter
- build_json = ::API::Entities::JobRequest::Response.new(presented_build).to_json
+ build_json = ::API::Entities::Ci::JobRequest::Response.new(presented_build).to_json
Result.new(build, build_json, true)
end
diff --git a/app/services/service_ping/submit_service.rb b/app/services/service_ping/submit_service.rb
index 06e4fcbaf32..5c03aa46e18 100644
--- a/app/services/service_ping/submit_service.rb
+++ b/app/services/service_ping/submit_service.rb
@@ -20,34 +20,50 @@ module ServicePing
def execute
return unless ServicePing::PermitDataCategoriesService.new.product_intelligence_enabled?
- usage_data = Gitlab::UsageData.data(force_refresh: true)
+ begin
+ usage_data = BuildPayloadService.new.execute
+ raw_usage_data, response = submit_usage_data_payload(usage_data)
+ rescue StandardError
+ return unless Gitlab::CurrentSettings.usage_ping_enabled?
+
+ usage_data = Gitlab::UsageData.data(force_refresh: true)
+ raw_usage_data, response = submit_usage_data_payload(usage_data)
+ end
- raise SubmissionError, 'Usage data is blank' if usage_data.blank?
+ version_usage_data_id = response.dig('conv_index', 'usage_data_id') || response.dig('dev_ops_score', 'usage_data_id')
- raw_usage_data = save_raw_usage_data(usage_data)
+ unless version_usage_data_id.is_a?(Integer) && version_usage_data_id > 0
+ raise SubmissionError, "Invalid usage_data_id in response: #{version_usage_data_id}"
+ end
+
+ raw_usage_data.update_version_metadata!(usage_data_id: version_usage_data_id)
+
+ store_metrics(response)
+ end
+
+ private
- response = Gitlab::HTTP.post(
+ def submit_payload(usage_data)
+ Gitlab::HTTP.post(
url,
body: usage_data.to_json,
allow_local_requests: true,
headers: { 'Content-type' => 'application/json' }
)
+ end
- raise SubmissionError, "Unsuccessful response code: #{response.code}" unless response.success?
+ def submit_usage_data_payload(usage_data)
+ raise SubmissionError, 'Usage data is blank' if usage_data.blank?
- version_usage_data_id = response.dig('conv_index', 'usage_data_id') || response.dig('dev_ops_score', 'usage_data_id')
+ raw_usage_data = save_raw_usage_data(usage_data)
- unless version_usage_data_id.is_a?(Integer) && version_usage_data_id > 0
- raise SubmissionError, "Invalid usage_data_id in response: #{version_usage_data_id}"
- end
+ response = submit_payload(usage_data)
- raw_usage_data.update_version_metadata!(usage_data_id: version_usage_data_id)
+ raise SubmissionError, "Unsuccessful response code: #{response.code}" unless response.success?
- store_metrics(response)
+ [raw_usage_data, response]
end
- private
-
def save_raw_usage_data(usage_data)
RawUsageData.safe_find_or_create_by(recorded_at: usage_data[:recorded_at]) do |record|
record.payload = usage_data
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index 3942cfa4643..a91c12d01ad 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -74,7 +74,7 @@
= f.text_field :build_timeout_human_readable, class: 'form-control gl-form-input'
%p.form-text.text-muted
= html_escape(_('Jobs fail if they run longer than the timeout time. Input value is in seconds by default. Human readable input is also accepted, for example %{code_open}1 hour%{code_close}.')) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'timeout'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'set-a-limit-for-how-long-jobs-can-run'), target: '_blank'
- if can?(current_user, :update_max_artifacts_size, @project)
.form-group
@@ -94,7 +94,7 @@
.input-group-text /
%p.form-text.text-muted
= html_escape(_('The regular expression used to find test coverage output in the job log. For example, use %{regex} for Simplecov (Ruby). Leave blank to disable.')) % { regex: '<code>\(\d+.\d+%\)</code>'.html_safe }
- = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'test-coverage-parsing'), target: '_blank'
+ = link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'add-test-coverage-results-to-a-merge-request'), target: '_blank'
= f.submit _('Save changes'), class: "btn gl-button btn-confirm", data: { qa_selector: 'save_general_pipelines_changes_button' }