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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/clusters_list/components/clusters.vue25
-rw-r--r--app/assets/javascripts/clusters_list/components/node_error_help_text.vue53
-rw-r--r--app/assets/javascripts/clusters_list/constants.js43
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue12
-rw-r--r--app/assets/javascripts/diffs/components/edit_button.vue59
-rw-r--r--app/assets/javascripts/releases/components/app_edit_new.vue3
-rw-r--r--app/assets/javascripts/snippets/components/snippet_description_edit.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue32
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss2
-rw-r--r--app/assets/stylesheets/pages/tags.scss3
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss17
-rw-r--r--app/assets/stylesheets/utilities.scss5
-rw-r--r--app/helpers/gitlab_routing_helper.rb12
-rw-r--r--app/serializers/cluster_entity.rb1
-rw-r--r--app/serializers/cluster_serializer.rb1
-rw-r--r--app/serializers/diff_file_base_entity.rb14
-rw-r--r--app/services/audit_event_service.rb5
-rw-r--r--app/views/notify/_failed_builds.html.haml2
-rw-r--r--app/views/projects/tags/_tag.html.haml2
-rw-r--r--changelogs/unreleased/21654-ide-button-in-mr-diff-files.yml5
-rw-r--r--changelogs/unreleased/nfriend-fix-markdown-preview-on-new-release-page.yml5
-rw-r--r--changelogs/unreleased/sh-fix-pipeline-notification-email-warning.yml5
-rw-r--r--config/routes.rb1
-rw-r--r--doc/README.md2
-rw-r--r--doc/administration/audit_events.md1
-rw-r--r--doc/ci/img/gitlab_vault_workflow_v13_4.pngbin0 -> 47541 bytes
-rw-r--r--doc/ci/secrets/index.md25
-rw-r--r--doc/development/integrations/secure_partner_integration.md2
-rw-r--r--doc/development/redis.md6
-rw-r--r--doc/user/application_security/security_dashboard/img/instance_security_center_settings_v13_4.pngbin0 -> 87684 bytes
-rw-r--r--doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.pngbin62615 -> 87470 bytes
-rw-r--r--doc/user/application_security/security_dashboard/index.md58
-rw-r--r--doc/user/clusters/agent/index.md2
-rw-r--r--doc/user/permissions.md1
-rw-r--r--locale/gitlab.pot56
-rw-r--r--package.json2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb2
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb5
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb4
-rw-r--r--spec/features/projects/blobs/edit_spec.rb15
-rw-r--r--spec/features/projects/releases/user_creates_release_spec.rb18
-rw-r--r--spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap8
-rw-r--r--spec/frontend/clusters_list/components/clusters_spec.js18
-rw-r--r--spec/frontend/clusters_list/components/node_error_help_text_spec.js33
-rw-r--r--spec/frontend/clusters_list/mock_data.js10
-rw-r--r--spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap1
-rw-r--r--spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap2
-rw-r--r--spec/frontend/design_management/components/toolbar/__snapshots__/index_spec.js.snap1
-rw-r--r--spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap3
-rw-r--r--spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap1
-rw-r--r--spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap1
-rw-r--r--spec/frontend/diffs/components/diff_file_header_spec.js26
-rw-r--r--spec/frontend/diffs/components/edit_button_spec.js147
-rw-r--r--spec/frontend/grafana_integration/components/__snapshots__/grafana_integration_spec.js.snap2
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap1
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap1
-rw-r--r--spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap2
-rw-r--r--spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap1
-rw-r--r--spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap3
-rw-r--r--spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap1
-rw-r--r--spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap3
-rw-r--r--spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap2
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap2
-rw-r--r--spec/serializers/cluster_serializer_spec.rb1
-rw-r--r--spec/serializers/diff_file_base_entity_spec.rb55
-rw-r--r--yarn.lock8
74 files changed, 689 insertions, 167 deletions
diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue
index 7b53020fc49..c734da80f48 100644
--- a/app/assets/javascripts/clusters_list/components/clusters.vue
+++ b/app/assets/javascripts/clusters_list/components/clusters.vue
@@ -10,6 +10,7 @@ import {
GlTable,
} from '@gitlab/ui';
import AncestorNotice from './ancestor_notice.vue';
+import NodeErrorHelpText from './node_error_help_text.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import { CLUSTER_TYPES, STATUSES } from '../constants';
import { __, sprintf } from '~/locale';
@@ -26,6 +27,7 @@ export default {
GlSkeletonLoading,
GlSprintf,
GlTable,
+ NodeErrorHelpText,
},
directives: {
tooltip,
@@ -231,9 +233,12 @@ export default {
<gl-skeleton-loading v-else-if="loadingNodes" :lines="1" :class="contentAlignClasses" />
- <small v-else class="gl-font-sm gl-font-style-italic gl-text-gray-200">{{
- __('Unknown')
- }}</small>
+ <NodeErrorHelpText
+ v-else-if="item.kubernetes_errors"
+ :class="contentAlignClasses"
+ :error-type="item.kubernetes_errors.connection_error"
+ :popover-id="`nodeSizeError${item.id}`"
+ />
</template>
<template #cell(total_cpu)="{ item }">
@@ -250,6 +255,13 @@ export default {
</span>
<gl-skeleton-loading v-else-if="loadingNodes" :lines="1" :class="contentAlignClasses" />
+
+ <NodeErrorHelpText
+ v-else-if="item.kubernetes_errors"
+ :class="contentAlignClasses"
+ :error-type="item.kubernetes_errors.node_connection_error"
+ :popover-id="`nodeCpuError${item.id}`"
+ />
</template>
<template #cell(total_memory)="{ item }">
@@ -266,6 +278,13 @@ export default {
</span>
<gl-skeleton-loading v-else-if="loadingNodes" :lines="1" :class="contentAlignClasses" />
+
+ <NodeErrorHelpText
+ v-else-if="item.kubernetes_errors"
+ :class="contentAlignClasses"
+ :error-type="item.kubernetes_errors.metrics_connection_error"
+ :popover-id="`nodeMemoryError${item.id}`"
+ />
</template>
<template #cell(cluster_type)="{value}">
diff --git a/app/assets/javascripts/clusters_list/components/node_error_help_text.vue b/app/assets/javascripts/clusters_list/components/node_error_help_text.vue
new file mode 100644
index 00000000000..1a396694bc8
--- /dev/null
+++ b/app/assets/javascripts/clusters_list/components/node_error_help_text.vue
@@ -0,0 +1,53 @@
+<script>
+import { GlIcon, GlPopover } from '@gitlab/ui';
+import { CLUSTER_ERRORS } from '../constants';
+
+export default {
+ components: {
+ GlIcon,
+ GlPopover,
+ },
+ props: {
+ errorType: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ popoverId: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ errorContent() {
+ return CLUSTER_ERRORS[this.errorType] || CLUSTER_ERRORS.default;
+ },
+ },
+};
+</script>
+
+<template>
+ <div :id="popoverId">
+ <span class="gl-font-style-italic">
+ {{ errorContent.tableText }}
+ </span>
+
+ <gl-icon name="status_warning" :size="24" class="gl-p-2" />
+
+ <gl-popover :container="popoverId" :target="popoverId" placement="top" triggers="hover focus">
+ <template #title>
+ <span class="gl-display-block gl-text-left">{{ errorContent.title }}</span>
+ </template>
+
+ <p class="gl-text-left">{{ errorContent.description }}</p>
+
+ <p class="gl-text-left">{{ s__('ClusterIntegration|Troubleshooting tips:') }}</p>
+
+ <ul class="gl-text-left">
+ <li v-for="tip in errorContent.troubleshootingTips" :key="tip">
+ {{ tip }}
+ </li>
+ </ul>
+ </gl-popover>
+ </div>
+</template>
diff --git a/app/assets/javascripts/clusters_list/constants.js b/app/assets/javascripts/clusters_list/constants.js
index 3e8ef3151a6..f39678b73dc 100644
--- a/app/assets/javascripts/clusters_list/constants.js
+++ b/app/assets/javascripts/clusters_list/constants.js
@@ -1,4 +1,45 @@
-import { __ } from '~/locale';
+import { __, s__ } from '~/locale';
+
+export const CLUSTER_ERRORS = {
+ default: {
+ tableText: s__('ClusterIntegration|Unknown Error'),
+ title: s__('ClusterIntegration|Unknown Error'),
+ description: s__(
+ 'ClusterIntegration|An unknown error occurred while attempting to connect to Kubernetes.',
+ ),
+ troubleshootingTips: [
+ s__('ClusterIntegration|Check your cluster status'),
+ s__('ClusterIntegration|Make sure your API endpoint is correct'),
+ s__(
+ 'ClusterIntegration|Node calculations use the Kubernetes Metrics API. Make sure your cluster has metrics installed',
+ ),
+ ],
+ },
+ authentication_error: {
+ tableText: s__('ClusterIntegration|Unable to Authenticate'),
+ title: s__('ClusterIntegration|Authentication Error'),
+ description: s__('ClusterIntegration|GitLab failed to authenticate.'),
+ troubleshootingTips: [
+ s__('ClusterIntegration|Check your token'),
+ s__('ClusterIntegration|Check your CA certificate'),
+ ],
+ },
+ connection_error: {
+ tableText: s__('ClusterIntegration|Unable to Connect'),
+ title: s__('ClusterIntegration|Connection Error'),
+ description: s__('ClusterIntegration|GitLab failed to connect to the cluster.'),
+ troubleshootingTips: [
+ s__('ClusterIntegration|Check your cluster status'),
+ s__('ClusterIntegration|Make sure your API endpoint is correct'),
+ ],
+ },
+ http_error: {
+ tableText: s__('ClusterIntegration|Unable to Connect'),
+ title: s__('ClusterIntegration|HTTP Error'),
+ description: s__('ClusterIntegration|There was an HTTP error when connecting to your cluster.'),
+ troubleshootingTips: [s__('ClusterIntegration|Check your cluster status')],
+ },
+};
export const CLUSTER_TYPES = {
project_type: __('Project'),
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index fded391cc84..aab69172204 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -69,6 +69,11 @@ export default {
default: false,
},
},
+ data() {
+ return {
+ hasDropdownOpen: false,
+ };
+ },
computed: {
...mapGetters('diffs', ['diffHasExpandedDiscussions', 'diffHasDiscussions']),
diffContentIDSelector() {
@@ -179,6 +184,9 @@ export default {
}
}
},
+ setDropdownOpen(val) {
+ this.hasDropdownOpen = val;
+ },
},
};
</script>
@@ -187,6 +195,7 @@ export default {
<div
ref="header"
class="js-file-title file-title file-title-flex-parent"
+ :class="{ 'gl-z-dropdown-menu!': hasDropdownOpen }"
@click.self="handleToggleFile"
>
<div class="file-header-content">
@@ -273,11 +282,14 @@ export default {
v-if="!diffFile.deleted_file"
:can-current-user-fork="canCurrentUserFork"
:edit-path="diffFile.edit_path"
+ :ide-edit-path="diffFile.ide_edit_path"
:can-modify-blob="diffFile.can_modify_blob"
data-track-event="click_toggle_edit_button"
data-track-label="diff_toggle_edit_button"
data-track-property="diff_toggle_edit"
@showForkMessage="showForkMessage"
+ @open="setDropdownOpen(true)"
+ @close="setDropdownOpen(false)"
/>
</template>
diff --git a/app/assets/javascripts/diffs/components/edit_button.vue b/app/assets/javascripts/diffs/components/edit_button.vue
index ff1af5569dc..a0d66ae2994 100644
--- a/app/assets/javascripts/diffs/components/edit_button.vue
+++ b/app/assets/javascripts/diffs/components/edit_button.vue
@@ -1,10 +1,17 @@
<script>
-import { GlTooltipDirective, GlDeprecatedButton, GlIcon } from '@gitlab/ui';
+import { uniqueId } from 'lodash';
+import {
+ GlTooltipDirective,
+ GlIcon,
+ GlDeprecatedDropdown as GlDropdown,
+ GlDeprecatedDropdownItem as GlDropdownItem,
+} from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
- GlDeprecatedButton,
+ GlDropdown,
+ GlDropdownItem,
GlIcon,
},
directives: {
@@ -16,6 +23,11 @@ export default {
required: false,
default: '',
},
+ ideEditPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
canCurrentUserFork: {
type: Boolean,
required: true,
@@ -26,39 +38,62 @@ export default {
default: false,
},
},
+ data() {
+ return { tooltipId: uniqueId('edit_button_tooltip_') };
+ },
computed: {
tooltipTitle() {
if (this.isDisabled) {
return __("Can't edit as source branch was deleted");
}
- return __('Edit file');
+ return __('Edit file in...');
},
isDisabled() {
return !this.editPath;
},
},
methods: {
- handleEditClick(evt) {
+ handleShow(evt) {
+ // We must hide the tooltip because it is redundant and doesn't close itself
+ // when dropdown opens because we are still "focused".
+ this.$root.$emit('bv::hide::tooltip', this.tooltipId);
+
if (this.canCurrentUserFork && !this.canModifyBlob) {
evt.preventDefault();
this.$emit('showForkMessage');
+ } else {
+ this.$emit('open');
}
},
+ handleHide() {
+ this.$emit('close');
+ },
},
};
</script>
<template>
- <span v-gl-tooltip.top :title="tooltipTitle">
- <gl-deprecated-button
- :href="editPath"
+ <div v-gl-tooltip.top="{ title: tooltipTitle, id: tooltipId }" class="gl-display-flex">
+ <gl-dropdown
+ toggle-class="rounded-0"
:disabled="isDisabled"
:class="{ 'cursor-not-allowed': isDisabled }"
- class="rounded-0 js-edit-blob"
- @click.native="handleEditClick"
+ right
+ data-testid="edit_file"
+ @show="handleShow"
+ @hide="handleHide"
>
- <gl-icon name="pencil" />
- </gl-deprecated-button>
- </span>
+ <template #button-content>
+ <span class="gl-dropdown-toggle-text"><gl-icon name="pencil"/></span>
+ <gl-icon class="gl-dropdown-caret" name="chevron-down" aria-hidden="true" />
+ </template>
+ <gl-dropdown-item v-if="editPath" :href="editPath">{{
+ __('Edit in single-file editor')
+ }}</gl-dropdown-item>
+ <gl-dropdown-item v-if="ideEditPath" :href="ideEditPath">{{
+ __('Edit in Web IDE')
+ }}</gl-dropdown-item>
+ </gl-dropdown>
+ </div>
</template>
diff --git a/app/assets/javascripts/releases/components/app_edit_new.vue b/app/assets/javascripts/releases/components/app_edit_new.vue
index e1edf3d689d..e72599f1f7f 100644
--- a/app/assets/javascripts/releases/components/app_edit_new.vue
+++ b/app/assets/javascripts/releases/components/app_edit_new.vue
@@ -150,7 +150,7 @@ export default {
/>
</div>
</gl-form-group>
- <gl-form-group>
+ <gl-form-group data-testid="release-notes">
<label for="release-notes">{{ __('Release notes') }}</label>
<div class="bordered-box pr-3 pl-3">
<markdown-field
@@ -158,6 +158,7 @@ export default {
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
:add-spacing-classes="false"
+ :textarea-value="releaseNotes"
class="gl-mt-3 gl-mb-3"
>
<template #textarea>
diff --git a/app/assets/javascripts/snippets/components/snippet_description_edit.vue b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
index 737845d09b8..5e6caf27bdd 100644
--- a/app/assets/javascripts/snippets/components/snippet_description_edit.vue
+++ b/app/assets/javascripts/snippets/components/snippet_description_edit.vue
@@ -49,6 +49,7 @@ export default {
:add-spacing-classes="false"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
+ :textarea-value="value"
>
<template #textarea>
<textarea
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index a48c279d0e3..5a465b7cdd8 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -84,7 +84,37 @@ export default {
required: false,
default: false,
},
- // This prop is used as a fallback in case if textarea.elm is undefined
+
+ /**
+ * This prop is used as a fallback if the value of the textarea can't be
+ * retreived using `this.$slots.textarea[0]?.elm?`.
+ *
+ * This happens when the `textarea` slot is defined like this:
+ *
+ * ```html
+ * <markdown-field>
+ * <template #textarea>
+ * <textarea></textarea>
+ * </template>
+ * </markdown-field>
+ * ```
+ *
+ * ... as opposed to this:
+ *
+ * ```html
+ * <markdown-field>
+ * <textarea slot="textarea">
+ * </markdown-field>
+ * ```
+ *
+ * When using `<template #textarea>` as shown above in example #1,
+ * it's important to **always** provide a value to this prop.
+ * If `textareaValue` isn't provided, this component will not
+ * show a preview when the "Preview" tab is clicked - it
+ * will always show "Nothing to preview."
+ *
+ * For more info, see https://github.com/vuejs/vue/issues/10450.
+ */
textareaValue: {
type: String,
required: false,
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index f706b615e7e..965769d28f7 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -57,9 +57,7 @@
@import './pages/sherlock';
@import './pages/status';
@import './pages/storage_quota';
-@import './pages/tags';
@import './pages/tree';
@import './pages/trials';
-@import './pages/ui_dev_kit';
@import './pages/users';
@import './pages/wiki';
diff --git a/app/assets/stylesheets/pages/tags.scss b/app/assets/stylesheets/pages/tags.scss
deleted file mode 100644
index a6d30522ff7..00000000000
--- a/app/assets/stylesheets/pages/tags.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.tag-release-link {
- color: $blue-600 !important;
-}
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
deleted file mode 100644
index 288da4da5c3..00000000000
--- a/app/assets/stylesheets/pages/ui_dev_kit.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-.gitlab-ui-dev-kit {
- > h2 {
- margin: 35px 0 20px;
- font-weight: $gl-font-weight-bold;
- }
-
- .example {
- padding: 15px;
- border: 1px dashed $gray-100;
- margin-bottom: 15px;
-
- &::before {
- content: 'Example';
- color: $ui-dev-kit-example-color;
- }
- }
-}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 9c666331c4f..9277269c730 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -156,3 +156,8 @@
display: none;
}
}
+
+// This utility is used to force the z-index to match that of dropdown menu's
+.gl-z-dropdown-menu\! {
+ z-index: 300 !important;
+}
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index d71e6b4c004..7df6bef7914 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -343,6 +343,18 @@ module GitlabRoutingHelper
Gitlab::UrlBuilder.wiki_page_url(wiki, page, only_path: true, **options)
end
+ def gitlab_ide_merge_request_path(merge_request)
+ target_project = merge_request.target_project
+ source_project = merge_request.source_project
+ params = {}
+
+ if target_project != source_project
+ params = { target_project: target_project.full_path }
+ end
+
+ ide_merge_request_path(source_project.namespace, source_project, merge_request, params)
+ end
+
private
def snippet_query_params(snippet, *args)
diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb
index eea0acdc11b..9872bbf80b5 100644
--- a/app/serializers/cluster_entity.rb
+++ b/app/serializers/cluster_entity.rb
@@ -6,6 +6,7 @@ class ClusterEntity < Grape::Entity
expose :cluster_type
expose :enabled
expose :environment_scope
+ expose :id
expose :name
expose :nodes
expose :provider_type
diff --git a/app/serializers/cluster_serializer.rb b/app/serializers/cluster_serializer.rb
index 700a46040e3..f71591612a6 100644
--- a/app/serializers/cluster_serializer.rb
+++ b/app/serializers/cluster_serializer.rb
@@ -12,6 +12,7 @@ class ClusterSerializer < BaseSerializer
:environment_scope,
:gitlab_managed_apps_logs_path,
:enable_advanced_logs_querying,
+ :id,
:kubernetes_errors,
:name,
:nodes,
diff --git a/app/serializers/diff_file_base_entity.rb b/app/serializers/diff_file_base_entity.rb
index 9f27191c3c8..596f5d686da 100644
--- a/app/serializers/diff_file_base_entity.rb
+++ b/app/serializers/diff_file_base_entity.rb
@@ -34,7 +34,7 @@ class DiffFileBaseEntity < Grape::Entity
expose :edit_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
merge_request = options[:merge_request]
- next unless merge_request.merged? || merge_request.source_branch_exists?
+ next unless has_edit_path?(merge_request)
target_project, target_branch = edit_project_branch_options(merge_request)
@@ -43,6 +43,14 @@ class DiffFileBaseEntity < Grape::Entity
project_edit_blob_path(target_project, tree_join(target_branch, diff_file.new_path), options)
end
+ expose :ide_edit_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
+ merge_request = options[:merge_request]
+
+ next unless has_edit_path?(merge_request)
+
+ gitlab_ide_merge_request_path(merge_request)
+ end
+
expose :old_path_html do |diff_file|
old_path, _ = mark_inline_diffs(diff_file.old_path, diff_file.new_path)
old_path
@@ -125,4 +133,8 @@ class DiffFileBaseEntity < Grape::Entity
[merge_request.target_project, merge_request.target_branch]
end
end
+
+ def has_edit_path?(merge_request)
+ merge_request.merged? || merge_request.source_branch_exists?
+ end
end
diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb
index d7630dbdac9..9802f48ae7e 100644
--- a/app/services/audit_event_service.rb
+++ b/app/services/audit_event_service.rb
@@ -53,7 +53,6 @@ class AuditEventService
private
- attr_accessor :authentication_event
attr_reader :ip_address
def build_author(author)
@@ -99,11 +98,11 @@ class AuditEventService
end
def mark_as_authentication_event!
- self.authentication_event = true
+ @authentication_event = true
end
def authentication_event?
- authentication_event
+ @authentication_event
end
def log_security_event_to_database
diff --git a/app/views/notify/_failed_builds.html.haml b/app/views/notify/_failed_builds.html.haml
index cde0ac21d6d..11cbd700258 100644
--- a/app/views/notify/_failed_builds.html.haml
+++ b/app/views/notify/_failed_builds.html.haml
@@ -6,7 +6,7 @@
#{'build'.pluralize(failed.size)}.
%tr.table-warning
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; border: 1px solid #ededed; border-bottom: 0; border-radius: 4px 4px 0 0; overflow: hidden; background-color: #fdf4f6; color: #d22852; font-size: 14px; line-height: 1.4; text-align: center; padding: 8px 16px;" }
- Logs may contain sensitive data. Please consider before forwarding this email.
+ Failed builds
%tr.section
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 0 16px; border: 1px solid #ededed; border-radius: 4px; overflow: hidden; border-top: 0; border-radius: 0 0 4px 4px;" }
%table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width: 100%; border-collapse: collapse;" }
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index dba9b20fcff..4f14ee4d044 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -24,7 +24,7 @@
.text-secondary
= sprite_icon("rocket", size: 12)
= _("Release")
- = link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'tag-release-link'
+ = link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'gl-text-blue-600!'
- if release.description.present?
.md.gl-mt-3
= markdown_field(release, :description)
diff --git a/changelogs/unreleased/21654-ide-button-in-mr-diff-files.yml b/changelogs/unreleased/21654-ide-button-in-mr-diff-files.yml
new file mode 100644
index 00000000000..6c56b39bc0f
--- /dev/null
+++ b/changelogs/unreleased/21654-ide-button-in-mr-diff-files.yml
@@ -0,0 +1,5 @@
+---
+title: Add Web IDE as dropdown item to diff file edit
+merge_request: 42275
+author:
+type: changed
diff --git a/changelogs/unreleased/nfriend-fix-markdown-preview-on-new-release-page.yml b/changelogs/unreleased/nfriend-fix-markdown-preview-on-new-release-page.yml
new file mode 100644
index 00000000000..b6854e68776
--- /dev/null
+++ b/changelogs/unreleased/nfriend-fix-markdown-preview-on-new-release-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Markdown "Preview" tab on New/Edit Release and New Snippet pages
+merge_request: 42640
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-pipeline-notification-email-warning.yml b/changelogs/unreleased/sh-fix-pipeline-notification-email-warning.yml
new file mode 100644
index 00000000000..a9f5ddcef33
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-pipeline-notification-email-warning.yml
@@ -0,0 +1,5 @@
+---
+title: Update pipeline failed notification e-mail warning
+merge_request: 42736
+author:
+type: fixed
diff --git a/config/routes.rb b/config/routes.rb
index 9bd68bfeef6..5dbb24dcc7c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -122,6 +122,7 @@ Rails.application.routes.draw do
get 'ide' => 'ide#index'
get 'ide/*vueroute' => 'ide#index', format: false
+ get 'ide/project/:namespace/:project/merge_requests/:id' => 'ide#index', format: false, as: :ide_merge_request
draw :operations
draw :jira_connect
diff --git a/doc/README.md b/doc/README.md
index efae2cdd3ff..52123c1db66 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -295,7 +295,7 @@ The following documentation relates to the DevOps **Secure** stage:
| [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. |
| [Dynamic Application Security Testing (DAST)](user/application_security/dast/index.md) **(ULTIMATE)** | Analyze running web applications for known vulnerabilities. |
| [Group Security Dashboard](user/application_security/security_dashboard/index.md#group-security-dashboard) **(ULTIMATE)** | View vulnerabilities in all the projects in a group and its subgroups. |
-| [Instance Security Dashboard](user/application_security/security_dashboard/index.md#instance-security-dashboard) **(ULTIMATE)** | View vulnerabilities in all the projects you're interested in. |
+| [Instance Security Center](user/application_security/security_dashboard/index.md#instance-security-center) **(ULTIMATE)** | View vulnerabilities in all the projects you're interested in. |
| [License Compliance](user/compliance/license_compliance/index.md) **(ULTIMATE)** | Search your project's dependencies for their licenses. |
| [Pipeline Security](user/application_security/security_dashboard/index.md#pipeline-security) **(ULTIMATE)** | View the security reports for your project's pipelines. |
| [Project Security Dashboard](user/application_security/security_dashboard/index.md#project-security-dashboard) **(ULTIMATE)** | View the latest security reports for your project. |
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 099346b2b0b..7b6327838d3 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -183,6 +183,7 @@ the steps bellow.
CAUTION: **Warning:**
This feature might not be available to you. Check the **version history** note above for details.
+If available, you can enable it with a [feature flag](#enable-or-disable-audit-log-export-to-csv).
Export to CSV allows customers to export the current filter view of your audit log as a
CSV file,
diff --git a/doc/ci/img/gitlab_vault_workflow_v13_4.png b/doc/ci/img/gitlab_vault_workflow_v13_4.png
new file mode 100644
index 00000000000..80d07362bf4
--- /dev/null
+++ b/doc/ci/img/gitlab_vault_workflow_v13_4.png
Binary files differ
diff --git a/doc/ci/secrets/index.md b/doc/ci/secrets/index.md
index 6d561fe00a3..09aeebcc7cc 100644
--- a/doc/ci/secrets/index.md
+++ b/doc/ci/secrets/index.md
@@ -17,23 +17,36 @@ Unlike CI variables, which are always presented to a job, secrets must be explic
required by a job. Read [GitLab CI/CD pipeline configuration reference](../yaml/README.md#secrets)
for more information about the syntax.
-GitLab has selected [Vault by Hashicorp](https://www.vaultproject.io) as the
+GitLab has selected [Vault by HashiCorp](https://www.vaultproject.io) as the
first supported provider, and [KV-V2](https://www.vaultproject.io/docs/secrets/kv/kv-v2)
as the first supported secrets engine.
GitLab authenticates using Vault's
-[JWT Auth method](https://www.vaultproject.io/docs/auth/jwt#jwt-authentication), using
+[JSON Web Token (JWT) authentication method](https://www.vaultproject.io/docs/auth/jwt#jwt-authentication), using
the [JSON Web Token](https://gitlab.com/gitlab-org/gitlab/-/issues/207125) (`CI_JOB_JWT`)
introduced in GitLab 12.10.
You must [configure your Vault server](#configure-your-vault-server) before you
can use [use Vault secrets in a CI job](#use-vault-secrets-in-a-ci-job).
+The flow for using GitLab with HashiCorp Vault
+is summarized by this diagram:
+
+![Flow between GitLab and HashiCorp](../img/gitlab_vault_workflow_v13_4.png "How GitLab CI_JOB_JWT works with HashiCorp Vault")
+
+1. Configure your vault and secrets.
+1. Generate your JWT and provide it to your CI job.
+1. Runner contacts HashiCorp Vault and authenticates using the JWT.
+1. HashiCorp Vault verifies the JWT.
+1. HashiCorp Vault checks the bounded claims and attaches policies.
+1. HashiCorp Vault returns the token.
+1. Runner reads secrets from the HashiCoupr Vault.
+
NOTE: **Note:**
-Read the [Authenticating and Reading Secrets With Hashicorp Vault](../examples/authenticating-with-hashicorp-vault/index.md)
-tutorial for a version of this feature that is available to all
+Read the [Authenticating and Reading Secrets With HashiCorp Vault](../examples/authenticating-with-hashicorp-vault/index.md)
+tutorial for a version of this feature. It's available to all
subscription levels, supports writing secrets to and deleting secrets from Vault,
-and multiple secrets engines.
+and supports multiple secrets engines.
## Configure your Vault server
@@ -149,7 +162,7 @@ generated by this GitLab instance may be allowed to authenticate using this role
For a full list of `CI_JOB_JWT` claims, read the
[How it works](../examples/authenticating-with-hashicorp-vault/index.md#how-it-works) section of the
-[Authenticating and Reading Secrets With Hashicorp Vault](../examples/authenticating-with-hashicorp-vault/index.md) tutorial.
+[Authenticating and Reading Secrets With HashiCorp Vault](../examples/authenticating-with-hashicorp-vault/index.md) tutorial.
You can also specify some attributes for the resulting Vault tokens, such as time-to-live,
IP address range, and number of uses. The full list of options is available in
diff --git a/doc/development/integrations/secure_partner_integration.md b/doc/development/integrations/secure_partner_integration.md
index 830cb84e257..36a40162184 100644
--- a/doc/development/integrations/secure_partner_integration.md
+++ b/doc/development/integrations/secure_partner_integration.md
@@ -44,7 +44,7 @@ best place to integrate your own product and its results into GitLab.
- If certain policies (such as [merge request approvals](../../user/project/merge_requests/merge_request_approvals.md))
are in place for a project, developers must resolve specific findings or get
an approval from a specific list of people.
-- The [security dashboard](../../user/application_security/security_dashboard/index.md#gitlab-security-dashboard)
+- The [security dashboard](../../user/application_security/security_dashboard/index.md)
also shows results which can developers can use to quickly see all the
vulnerabilities that need to be addressed in the code.
- When the developer reads the details about a vulnerability, they are
diff --git a/doc/development/redis.md b/doc/development/redis.md
index d205082b9c6..502bb656c22 100644
--- a/doc/development/redis.md
+++ b/doc/development/redis.md
@@ -96,10 +96,14 @@ requests that read the most data from the cache, we can just sort by
### The slow log
+TIP: **Tip:**
+There is a [video showing how to see the slow log](https://youtu.be/BBI68QuYRH8) (GitLab internal)
+on GitLab.com
+
On GitLab.com, entries from the [Redis
slow log](https://redis.io/commands/slowlog) are available in the
`pubsub-redis-inf-gprd*` index with the [`redis.slowlog`
-tag](https://log.gprd.gitlab.net/app/kibana#/discover?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-1d,to:now))&_a=(columns:!(json.type,json.command,json.exec_time),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:AWSQX_Vf93rHTYrsexmk,key:json.tag,negate:!f,params:(query:redis.slowlog),type:phrase),query:(match:(json.tag:(query:redis.slowlog,type:phrase))))),index:AWSQX_Vf93rHTYrsexmk)).
+tag](https://log.gprd.gitlab.net/app/kibana#/discover?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-1d,to:now))&_a=(columns:!(json.type,json.command,json.exec_time_s),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:AWSQX_Vf93rHTYrsexmk,key:json.tag,negate:!f,params:(query:redis.slowlog),type:phrase),query:(match:(json.tag:(query:redis.slowlog,type:phrase))))),index:AWSQX_Vf93rHTYrsexmk)).
This shows commands that have taken a long time and may be a performance
concern.
diff --git a/doc/user/application_security/security_dashboard/img/instance_security_center_settings_v13_4.png b/doc/user/application_security/security_dashboard/img/instance_security_center_settings_v13_4.png
new file mode 100644
index 00000000000..d7d5961087c
--- /dev/null
+++ b/doc/user/application_security/security_dashboard/img/instance_security_center_settings_v13_4.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.png b/doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.png
index d010adcc90c..5e52bcc650a 100644
--- a/doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.png
+++ b/doc/user/application_security/security_dashboard/img/instance_security_dashboard_v13_4.png
Binary files differ
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index 8c461e27e70..974131e7683 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -5,21 +5,26 @@ group: Threat Insights
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/#designated-technical-writers
---
-# GitLab Security Dashboard **(ULTIMATE)**
+# GitLab Security Dashboard, Security Center, and Vulnerability Reports **(ULTIMATE)**
-The Security Dashboard is a good place to get an overview of all the security
-vulnerabilities in your groups, projects, and pipelines.
+GitLab provides a comprehensive set of features for viewing and managing vulnerabilities:
+
+- Security dashboards: An overview of the security status in your instance, groups, and projects.
+- Vulnerability reports: Detailed lists of all vulnerabilities for the instance, group, project, or
+ pipeline. This is where you triage and manage vulnerabilities.
+- Security Center: A dedicated area for vulnerability management at the instance level. This
+ includes a security dashboard, vulnerability report, and settings.
You can also drill down into a vulnerability and get extra information. This includes the project it
comes from, any related file(s), and metadata that helps you analyze the risk it poses. You can also
dismiss a vulnerability or create an issue for it.
-To benefit from the Security Dashboard you must first configure one of the
+To benefit from these features, you must first configure one of the
[security scanners](../index.md).
## Supported reports
-The Security Dashboard displays vulnerabilities detected by scanners such as:
+The vulnerability report displays vulnerabilities detected by scanners such as:
- [Container Scanning](../container_scanning/index.md)
- [Dynamic Application Security Testing](../dast/index.md)
@@ -29,7 +34,7 @@ The Security Dashboard displays vulnerabilities detected by scanners such as:
## Requirements
-To use the instance, group, project, or pipeline security dashboard:
+To use the security dashboards and vulnerability reports:
1. At least one project inside a group must be configured with at least one of
the [supported reports](#supported-reports).
@@ -112,38 +117,43 @@ Next to the timeline chart is a list of projects, grouped and sorted by the seve
Projects with no vulnerability tests configured will not appear in the list. Additionally, dismissed
vulnerabilities are excluded.
-Navigate to the group's [Vulnerability Report](#vulnerability-list) to view the vulnerabilities found.
+Navigate to the group's [vulnerability report](#vulnerability-report) to view the vulnerabilities found.
+
+## Instance Security Center
-## Instance Security Dashboard
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3426) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.4.
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/6953) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.8.
+The Security Center is where you manage vulnerabilities for your instance. It displays the
+vulnerabilities present in the default branches of all the projects you configure. It includes the
+following:
-At the instance level, the Security Dashboard displays the vulnerabilities present in the default
-branches of all the projects you configure to display on the dashboard. It includes all the
-[group Security Dashboard's](#group-security-dashboard)
-features.
+- The [group security dashboard's](#group-security-dashboard) features.
+- A [vulnerability report](#vulnerability-report).
+- A dedicated settings area to configure which projects to display.
![Instance Security Dashboard with projects](img/instance_security_dashboard_v13_4.png)
-You can access the Instance Security Dashboard from the menu
+You can access the Instance Security Center from the menu
bar at the top of the page. Under **More**, select **Security**.
-![Instance Security Dashboard navigation link](img/instance_security_dashboard_link_v12_4.png)
+![Instance Security Center navigation link](img/instance_security_dashboard_link_v12_4.png)
-The dashboard is empty before you add projects to it.
+The dashboard and vulnerability report are empty before you add projects.
-![Uninitialized Instance Security Dashboard](img/instance_security_dashboard_empty_v13_4.png)
+![Uninitialized Instance Security Center](img/instance_security_dashboard_empty_v13_4.png)
-### Adding projects to the dashboard
+### Adding projects to the Security Center
-To add projects to the dashboard:
+To add projects to the Security Center:
1. Click **Settings** in the left navigation bar or click the **Add projects** button.
1. Search for and add one or more projects using the **Search your projects** field.
1. Click the **Add projects** button.
-After you add projects, the Security Dashboard displays the vulnerabilities found in those projects'
-default branches.
+![Adding projects to Instance Security Center](img/instance_security_center_settings_v13_4.png)
+
+After you add projects, the security dashboard and vulnerability report display the vulnerabilities
+found in those projects' default branches.
## Export vulnerabilities
@@ -192,14 +202,14 @@ When using [Auto DevOps](../../../topics/autodevops/index.md), use
[special environment variables](../../../topics/autodevops/customize.md#environment-variables)
to configure daily security scans.
-## Vulnerability list
+## Vulnerability report
-Each dashboard's vulnerability list contains vulnerabilities from the latest scans that were merged
+Each vulnerability report contains vulnerabilities from the latest scans that were merged
into the default branch.
![Vulnerability Report](img/group_vulnerability_report_v13_4.png)
-You can filter which vulnerabilities the Security Dashboard displays by:
+You can filter which vulnerabilities the vulnerability report displays by:
- Status
- Severity
diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md
index 7b745577cc4..98d76e51cc0 100644
--- a/doc/user/clusters/agent/index.md
+++ b/doc/user/clusters/agent/index.md
@@ -8,8 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223061) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
-## Goals
-
The [GitLab Kubernetes Agent](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent) is an active in-cluster component for solving GitLab and Kubernetes integration tasks in a secure and cloud native way.
Features:
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index e2baac1a962..eeaa759b193 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -159,6 +159,7 @@ The following table depicts the various user permission levels in a project.
| Remove fork relationship | | | | | ✓ |
| Delete project | | | | | ✓ |
| Archive project | | | | | ✓ |
+| Export project | | | | ✓ | ✓ |
| Delete issues | | | | | ✓ |
| Delete pipelines | | | | | ✓ |
| Delete merge request | | | | | ✓ |
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d0d0606a34e..1a3525408c6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5374,6 +5374,9 @@ msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch zone machine types: %{error}"
msgstr ""
+msgid "ClusterIntegration|An unknown error occurred while attempting to connect to Kubernetes."
+msgstr ""
+
msgid "ClusterIntegration|Any project namespaces"
msgstr ""
@@ -5389,6 +5392,9 @@ msgstr ""
msgid "ClusterIntegration|Authenticate with Amazon Web Services"
msgstr ""
+msgid "ClusterIntegration|Authentication Error"
+msgstr ""
+
msgid "ClusterIntegration|Base domain"
msgstr ""
@@ -5407,6 +5413,15 @@ msgstr ""
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
+msgid "ClusterIntegration|Check your CA certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Check your cluster status"
+msgstr ""
+
+msgid "ClusterIntegration|Check your token"
+msgstr ""
+
msgid "ClusterIntegration|Choose the %{startLink}security group %{externalLinkIcon} %{endLink} to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets."
msgstr ""
@@ -5449,6 +5464,9 @@ msgstr ""
msgid "ClusterIntegration|Connect existing cluster"
msgstr ""
+msgid "ClusterIntegration|Connection Error"
+msgstr ""
+
msgid "ClusterIntegration|Copy API URL"
msgstr ""
@@ -5605,6 +5623,12 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner connects to the repository and executes CI/CD jobs, pushing results back and deploying applications to production."
msgstr ""
+msgid "ClusterIntegration|GitLab failed to authenticate."
+msgstr ""
+
+msgid "ClusterIntegration|GitLab failed to connect to the cluster."
+msgstr ""
+
msgid "ClusterIntegration|GitLab-managed cluster"
msgstr ""
@@ -5626,6 +5650,9 @@ msgstr ""
msgid "ClusterIntegration|Group cluster"
msgstr ""
+msgid "ClusterIntegration|HTTP Error"
+msgstr ""
+
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
@@ -5770,6 +5797,9 @@ msgstr ""
msgid "ClusterIntegration|Machine type"
msgstr ""
+msgid "ClusterIntegration|Make sure your API endpoint is correct"
+msgstr ""
+
msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
@@ -5821,6 +5851,9 @@ msgstr ""
msgid "ClusterIntegration|No zones matched your search"
msgstr ""
+msgid "ClusterIntegration|Node calculations use the Kubernetes Metrics API. Make sure your cluster has metrics installed"
+msgstr ""
+
msgid "ClusterIntegration|Number of nodes"
msgstr ""
@@ -6088,6 +6121,9 @@ msgstr ""
msgid "ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid."
msgstr ""
+msgid "ClusterIntegration|There was an HTTP error when connecting to your cluster."
+msgstr ""
+
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
@@ -6115,9 +6151,21 @@ msgstr ""
msgid "ClusterIntegration|To use a new project, first create one on %{docsLinkStart}Google Cloud Platform%{docsLinkEnd}."
msgstr ""
+msgid "ClusterIntegration|Troubleshooting tips:"
+msgstr ""
+
+msgid "ClusterIntegration|Unable to Authenticate"
+msgstr ""
+
+msgid "ClusterIntegration|Unable to Connect"
+msgstr ""
+
msgid "ClusterIntegration|Uninstall %{appTitle}"
msgstr ""
+msgid "ClusterIntegration|Unknown Error"
+msgstr ""
+
msgid "ClusterIntegration|Update %{appTitle}"
msgstr ""
@@ -9228,7 +9276,7 @@ msgstr ""
msgid "Edit environment"
msgstr ""
-msgid "Edit file"
+msgid "Edit file in..."
msgstr ""
msgid "Edit files in the editor and commit changes here"
@@ -9243,6 +9291,12 @@ msgstr ""
msgid "Edit identity for %{user_name}"
msgstr ""
+msgid "Edit in Web IDE"
+msgstr ""
+
+msgid "Edit in single-file editor"
+msgstr ""
+
msgid "Edit issues"
msgstr ""
diff --git a/package.json b/package.json
index ca76e4cab0b..307c24c80ce 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.5",
"@gitlab/svgs": "1.164.0",
- "@gitlab/ui": "21.3.1",
+ "@gitlab/ui": "21.4.2",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-1",
"@sentry/browser": "^5.22.3",
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
index f96b424d233..5672060a953 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
@@ -3,7 +3,7 @@
module QA
RSpec.describe 'Create' do
describe 'Push mirror a repository over HTTP' do
- it 'configures and syncs LFS objects for a (push) mirrored repository', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/414' do
+ it 'configures and syncs LFS objects for a (push) mirrored repository', :requires_admin, testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/414' do
Runtime::Feature.enable_and_verify('push_mirror_syncs_lfs')
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
index 326647b25f7..8de739f1559 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/create_and_process_pipeline_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :docker, :runner do
+ RSpec.describe 'Verify', :runner do
describe 'Pipeline creation and processing' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:max_wait) { 30 }
diff --git a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
index a296d60b27c..9ce87f353d0 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :docker, :runner do
+ RSpec.describe 'Verify', :runner do
describe 'Runner registration' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let!(:runner) do
diff --git a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
index f4edaaa84a8..5bfc88e45f2 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/testing/view_code_coverage_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Verify', :docker, :runner do
+ RSpec.describe 'Verify', :runner do
describe 'Code coverage statistics' do
let(:simplecov) { '\(\d+.\d+\%\) covered' }
let(:executor) { "qa-runner-#{Time.now.to_i}" }
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
index a617f3b3e29..4ca356c9b65 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :docker, :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages do
describe 'Maven Repository' do
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
index e97ede35610..43c708093b3 100644
--- a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :docker, :orchestrated, :packages do
+ RSpec.describe 'Package', :orchestrated, :packages do
describe 'NPM registry' do
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
index 18eb52830a2..abac4f2b91d 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
@@ -3,7 +3,7 @@
require 'digest/sha1'
module QA
- RSpec.describe 'Release', :docker, :runner do
+ RSpec.describe 'Release', :runner do
describe 'Git clone using a deploy key' do
before do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
index 47a1b3b5670..ece45d093a7 100644
--- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Release', :docker, :runner, :reliable do
+ RSpec.describe 'Release', :runner, :reliable do
describe 'Parent-child pipelines dependent relationship' do
let!(:project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
index 9eb81244aa4..38cee0e62ca 100644
--- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Release', :docker, :runner, :reliable do
+ RSpec.describe 'Release', :runner, :reliable do
describe 'Parent-child pipelines independent relationship' do
let!(:project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index 0e65cb358da..2a3f7d62828 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -26,7 +26,10 @@ RSpec.describe 'a maintainer edits files on a source-branch of an MR from a fork
visit project_merge_request_path(target_project, merge_request)
click_link 'Changes'
wait_for_requests
- first('.js-file-title').find('.js-edit-blob').click
+ within first('.js-file-title') do
+ find('[data-testid="edit_file"]').click
+ click_link 'Edit in single-file editor'
+ end
wait_for_requests
end
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 7a3a14e61e3..b4c06535b68 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
visit diffs_project_merge_request_path(project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
- expect(page).to have_selector("[id=\"#{changelog_id}\"] a.js-edit-blob")
+ expect(page).to have_selector("[id=\"#{changelog_id}\"] [data-testid='edit_file']")
end
end
@@ -73,7 +73,7 @@ RSpec.describe 'Merge request > User sees diff', :js do
visit diffs_project_merge_request_path(project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
- find("[id=\"#{changelog_id}\"] .js-edit-blob").click
+ find("[id=\"#{changelog_id}\"] [data-testid=\"edit_file\"").click
expect(page).to have_selector('.js-fork-suggestion-button', count: 1)
expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1)
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 5aca994f53e..3949c70e718 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -23,6 +23,19 @@ RSpec.describe 'Editing file blob', :js do
def edit_and_commit(commit_changes: true)
wait_for_requests
find('.js-edit-blob').click
+
+ fill_and_commit(commit_changes)
+ end
+
+ def mr_edit_and_commit(commit_changes: true)
+ wait_for_requests
+ find('[data-testid="edit_file"]').click
+ click_link 'Edit in single-file editor'
+
+ fill_and_commit(commit_changes)
+ end
+
+ def fill_and_commit(commit_changes)
fill_editor(content: 'class NextFeature\\nend\\n')
if commit_changes
@@ -38,7 +51,7 @@ RSpec.describe 'Editing file blob', :js do
context 'from MR diff' do
before do
visit diffs_project_merge_request_path(project, merge_request)
- edit_and_commit
+ mr_edit_and_commit
end
it 'returns me to the mr' do
diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb
index 5d05a7e4c91..dd0d7338a26 100644
--- a/spec/features/projects/releases/user_creates_release_spec.rb
+++ b/spec/features/projects/releases/user_creates_release_spec.rb
@@ -108,6 +108,24 @@ RSpec.describe 'User creates release', :js do
end
end
+ context 'when the release notes "Preview" tab is clicked' do
+ before do
+ find_field('Release notes').click
+
+ fill_release_notes('**some** _markdown_ [content](https://example.com)')
+
+ click_on 'Preview'
+
+ wait_for_all_requests
+ end
+
+ it 'renders a preview of the release notes markdown' do
+ within('[data-testid="release-notes"]') do
+ expect(page).to have_text('some markdown content')
+ end
+ end
+ end
+
def fill_out_form_and_submit
fill_tag_name(tag_name)
diff --git a/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap b/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap
index 16e92bf505a..a65d1eae2e3 100644
--- a/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap
+++ b/spec/frontend/alert_settings/__snapshots__/alert_settings_form_spec.js.snap
@@ -26,7 +26,7 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
</gl-form-group-stub>
<gl-form-group-stub label=\\"Authorization key\\" label-for=\\"authorization-key\\" label-class=\\"label-bold\\">
<gl-form-input-group-stub value=\\"abcedfg123\\" predefinedoptions=\\"[object Object]\\" id=\\"authorization-key\\" readonly=\\"\\" class=\\"gl-mb-2\\"></gl-form-input-group-stub>
- <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub>
+ <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub>
<gl-modal-stub modalid=\\"authKeyModal\\" titletag=\\"h4\\" modalclass=\\"\\" size=\\"md\\" title=\\"Reset key\\" ok-title=\\"Reset key\\" ok-variant=\\"danger\\">
Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.
</gl-modal-stub>
@@ -35,13 +35,13 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
<gl-form-textarea-stub noresize=\\"true\\" id=\\"alert-json\\" disabled=\\"true\\" state=\\"true\\" placeholder=\\"Enter test alert JSON....\\" rows=\\"6\\" max-rows=\\"10\\"></gl-form-textarea-stub>
</gl-form-group-stub>
<div class=\\"gl-display-flex gl-justify-content-end\\">
- <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" disabled=\\"true\\">Test alert payload</gl-button-stub>
+ <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">Test alert payload</gl-button-stub>
</div>
<div class=\\"footer-block row-content-block gl-display-flex gl-justify-content-space-between\\">
- <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" disabled=\\"true\\">
+ <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
Cancel
</gl-button-stub>
- <gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" disabled=\\"true\\">
+ <gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\">
Save changes
</gl-button-stub>
</div>
diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js
index 628c35ae839..34d99473eb7 100644
--- a/spec/frontend/clusters_list/components/clusters_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_spec.js
@@ -164,18 +164,18 @@ describe('Clusters', () => {
});
it.each`
- nodeSize | lineNumber
- ${'Unknown'} | ${0}
- ${'1'} | ${1}
- ${'2'} | ${2}
- ${'1'} | ${3}
- ${'1'} | ${4}
- ${'Unknown'} | ${5}
- `('renders node size for each cluster', ({ nodeSize, lineNumber }) => {
+ nodeText | lineNumber
+ ${'Unable to Authenticate'} | ${0}
+ ${'1'} | ${1}
+ ${'2'} | ${2}
+ ${'1'} | ${3}
+ ${'1'} | ${4}
+ ${'Unknown Error'} | ${5}
+ `('renders node size for each cluster', ({ nodeText, lineNumber }) => {
const sizes = findTable().findAll('td:nth-child(3)');
const size = sizes.at(lineNumber);
- expect(size.text()).toBe(nodeSize);
+ expect(size.text()).toContain(nodeText);
expect(size.find(GlSkeletonLoading).exists()).toBe(false);
});
});
diff --git a/spec/frontend/clusters_list/components/node_error_help_text_spec.js b/spec/frontend/clusters_list/components/node_error_help_text_spec.js
new file mode 100644
index 00000000000..4d157b3a8ab
--- /dev/null
+++ b/spec/frontend/clusters_list/components/node_error_help_text_spec.js
@@ -0,0 +1,33 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlPopover } from '@gitlab/ui';
+import NodeErrorHelpText from '~/clusters_list/components/node_error_help_text.vue';
+
+describe('NodeErrorHelpText', () => {
+ let wrapper;
+
+ const createWrapper = propsData => {
+ wrapper = shallowMount(NodeErrorHelpText, { propsData, stubs: { GlPopover } });
+ return wrapper.vm.$nextTick();
+ };
+
+ const findPopover = () => wrapper.find(GlPopover);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it.each`
+ errorType | wrapperText | popoverText
+ ${'authentication_error'} | ${'Unable to Authenticate'} | ${'GitLab failed to authenticate'}
+ ${'connection_error'} | ${'Unable to Connect'} | ${'GitLab failed to connect to the cluster'}
+ ${'http_error'} | ${'Unable to Connect'} | ${'There was an HTTP error when connecting to your cluster'}
+ ${'default'} | ${'Unknown Error'} | ${'An unknown error occurred while attempting to connect to Kubernetes.'}
+ ${'unknown_error_type'} | ${'Unknown Error'} | ${'An unknown error occurred while attempting to connect to Kubernetes.'}
+ ${null} | ${'Unknown Error'} | ${'An unknown error occurred while attempting to connect to Kubernetes.'}
+ `('displays error text', ({ errorType, wrapperText, popoverText }) => {
+ return createWrapper({ errorType, popoverId: 'id' }).then(() => {
+ expect(wrapper.text()).toContain(wrapperText);
+ expect(findPopover().text()).toContain(popoverText);
+ });
+ });
+});
diff --git a/spec/frontend/clusters_list/mock_data.js b/spec/frontend/clusters_list/mock_data.js
index 48af3b91c94..ed32655d10e 100644
--- a/spec/frontend/clusters_list/mock_data.js
+++ b/spec/frontend/clusters_list/mock_data.js
@@ -6,6 +6,11 @@ export const clusterList = [
provider_type: 'gcp',
status: 'creating',
nodes: null,
+ kubernetes_errors: {
+ connection_error: 'authentication_error',
+ node_connection_error: 'connection_error',
+ metrics_connection_error: 'http_error',
+ },
},
{
name: 'My Cluster 2',
@@ -19,6 +24,7 @@ export const clusterList = [
usage: { cpu: '246155922n', memory: '1255212Ki' },
},
],
+ kubernetes_errors: {},
},
{
name: 'My Cluster 3',
@@ -36,6 +42,7 @@ export const clusterList = [
usage: { cpu: '307051934n', memory: '1379136Ki' },
},
],
+ kubernetes_errors: {},
},
{
name: 'My Cluster 4',
@@ -48,6 +55,7 @@ export const clusterList = [
usage: { cpu: '1missingCpuUnit', memory: '1missingMemoryUnit' },
},
],
+ kubernetes_errors: {},
},
{
name: 'My Cluster 5',
@@ -59,12 +67,14 @@ export const clusterList = [
status: { allocatable: { cpu: '1missingCpuUnit', memory: '1missingMemoryUnit' } },
},
],
+ kubernetes_errors: {},
},
{
name: 'My Cluster 6',
environment_scope: '*',
cluster_type: 'project_type',
status: 'cleanup_ongoing',
+ kubernetes_errors: {},
},
];
diff --git a/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
index 745a163951a..62b751ec59b 100644
--- a/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
+++ b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
@@ -56,6 +56,7 @@ exports[`Code navigation popover component renders popover 1`] = `
class="popover-body border-top"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="w-100"
data-testid="go-to-definition-btn"
diff --git a/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap b/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap
index a7d6145285c..f6a941fc99c 100644
--- a/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap
+++ b/spec/frontend/design_management/components/toolbar/__snapshots__/design_navigation_spec.js.snap
@@ -13,6 +13,7 @@ exports[`Design management pagination component renders navigation buttons 1`] =
class="ml-3 mr-3"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-previous-design"
disabled="true"
@@ -23,6 +24,7 @@ exports[`Design management pagination component renders navigation buttons 1`] =
/>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-next-design"
icon="angle-right"
diff --git a/spec/frontend/design_management/components/toolbar/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/components/toolbar/__snapshots__/index_spec.js.snap
index b286a74ebb8..adf713d0811 100644
--- a/spec/frontend/design_management/components/toolbar/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/components/toolbar/__snapshots__/index_spec.js.snap
@@ -41,6 +41,7 @@ exports[`Design management toolbar component renders design and updated data 1`]
/>
<gl-button-stub
+ buttontextclasses=""
category="primary"
href="/-/designs/306/7f747adcd4693afadbe968d7ba7d983349b9012d"
icon="download"
diff --git a/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap b/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap
index 3d7939df28e..eaa7460ae15 100644
--- a/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap
+++ b/spec/frontend/design_management/components/upload/__snapshots__/button_spec.js.snap
@@ -5,6 +5,7 @@ exports[`Design management upload button component renders inverted upload desig
isinverted="true"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
icon=""
size="small"
@@ -30,6 +31,7 @@ exports[`Design management upload button component renders inverted upload desig
exports[`Design management upload button component renders loading icon 1`] = `
<div>
<gl-button-stub
+ buttontextclasses=""
category="primary"
disabled="true"
icon=""
@@ -62,6 +64,7 @@ exports[`Design management upload button component renders loading icon 1`] = `
exports[`Design management upload button component renders upload design button 1`] = `
<div>
<gl-button-stub
+ buttontextclasses=""
category="primary"
icon=""
size="small"
diff --git a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
index b80b7fdb43e..07ba6c7309e 100644
--- a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
@@ -110,6 +110,7 @@ exports[`Design management index page designs renders designs list and header wi
class="qa-selector-toolbar gl-display-flex gl-align-items-center"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="gl-mr-4 js-select-all"
icon=""
diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
index c849e4d4ed6..8546f9fbf51 100644
--- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
@@ -67,6 +67,7 @@ exports[`Design management design index page renders design index 1`] = `
/>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="link-inherit-color gl-text-body gl-text-decoration-none gl-font-weight-bold gl-mb-4"
data-testid="resolved-comments"
diff --git a/spec/frontend/diffs/components/diff_file_header_spec.js b/spec/frontend/diffs/components/diff_file_header_spec.js
index a0cad32b9fb..817cb8dbf0c 100644
--- a/spec/frontend/diffs/components/diff_file_header_spec.js
+++ b/spec/frontend/diffs/components/diff_file_header_spec.js
@@ -76,6 +76,7 @@ describe('DiffFileHeader component', () => {
const findReplacedFileButton = () => wrapper.find({ ref: 'replacedFileButton' });
const findViewFileButton = () => wrapper.find({ ref: 'viewButton' });
const findCollapseIcon = () => wrapper.find({ ref: 'collapseIcon' });
+ const hasZDropdownMenuClass = () => wrapper.classes('gl-z-dropdown-menu!');
const findIconByName = iconName => {
const icons = wrapper.findAll(GlIcon).filter(w => w.props('name') === iconName);
@@ -151,6 +152,10 @@ describe('DiffFileHeader component', () => {
expect(wrapper.find(ClipboardButton).exists()).toBe(true);
});
+ it('should not have z dropdown menu class', () => {
+ expect(hasZDropdownMenuClass()).toBe(false);
+ });
+
describe('for submodule', () => {
const submoduleDiffFile = {
...diffFile,
@@ -303,6 +308,27 @@ describe('DiffFileHeader component', () => {
expect(wrapper.find(EditButton).exists()).toBe(true);
});
+ describe('when edit button opens', () => {
+ beforeEach(async () => {
+ createComponent({ addMergeRequestButtons: true });
+ wrapper.find(EditButton).vm.$emit('open');
+
+ await wrapper.vm.$nextTick();
+ });
+
+ it('should add z dropdown menu class when edit button opens', async () => {
+ expect(hasZDropdownMenuClass()).toBe(true);
+ });
+
+ it('when closes again, should remove class', async () => {
+ wrapper.find(EditButton).vm.$emit('close');
+
+ await wrapper.vm.$nextTick();
+
+ expect(hasZDropdownMenuClass()).toBe(false);
+ });
+ });
+
describe('view on environment button', () => {
it('is displayed when external url is provided', () => {
const externalUrl = 'link://to/external';
diff --git a/spec/frontend/diffs/components/edit_button_spec.js b/spec/frontend/diffs/components/edit_button_spec.js
index 71512c1c4af..cc425cc7301 100644
--- a/spec/frontend/diffs/components/edit_button_spec.js
+++ b/spec/frontend/diffs/components/edit_button_spec.js
@@ -1,15 +1,34 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlDeprecatedButton } from '@gitlab/ui';
+import { shallowMount, mount } from '@vue/test-utils';
+import { GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlIcon } from '@gitlab/ui';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import EditButton from '~/diffs/components/edit_button.vue';
-const editPath = 'test-path';
+jest.mock('lodash/uniqueId', () => (str = '') => `${str}fake`);
+
+const TOOLTIP_ID = 'edit_button_tooltip_fake';
+const EDIT_ITEM = {
+ href: 'test-path',
+ text: 'Edit in single-file editor',
+};
+const IDE_EDIT_ITEM = {
+ href: 'ide-test-path',
+ text: 'Edit in Web IDE',
+};
describe('EditButton', () => {
let wrapper;
- const createComponent = (props = {}) => {
- wrapper = shallowMount(EditButton, {
- propsData: { ...props },
+ const createComponent = (props = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(EditButton, {
+ propsData: {
+ editPath: EDIT_ITEM.href,
+ ideEditPath: IDE_EDIT_ITEM.href,
+ canCurrentUserFork: false,
+ ...props,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
});
};
@@ -17,59 +36,105 @@ describe('EditButton', () => {
wrapper.destroy();
});
- it('has correct href attribute', () => {
- createComponent({
- editPath,
- canCurrentUserFork: false,
- });
+ const getTooltip = () => getBinding(wrapper.element, 'gl-tooltip').value;
+ const findDropdown = () => wrapper.find(GlDeprecatedDropdown);
+ const parseDropdownItems = () =>
+ wrapper.findAll(GlDeprecatedDropdownItem).wrappers.map(x => ({
+ text: x.text(),
+ href: x.attributes('href'),
+ }));
+ const triggerShow = () => {
+ const event = new Event('');
+ jest.spyOn(event, 'preventDefault');
+
+ findDropdown().vm.$emit('show', event);
+
+ return event;
+ };
+
+ it.each`
+ props | expectedItems
+ ${{}} | ${[EDIT_ITEM, IDE_EDIT_ITEM]}
+ ${{ editPath: '' }} | ${[IDE_EDIT_ITEM]}
+ ${{ ideEditPath: '' }} | ${[EDIT_ITEM]}
+ `('should render items with=$props', ({ props, expectedItems }) => {
+ createComponent(props);
- expect(wrapper.find(GlDeprecatedButton).attributes('href')).toBe(editPath);
+ expect(parseDropdownItems()).toEqual(expectedItems);
});
- it('emits a show fork message event if current user can fork', () => {
- createComponent({
- editPath,
- canCurrentUserFork: true,
+ describe('with default', () => {
+ beforeEach(() => {
+ createComponent({}, mount);
});
- wrapper.find(GlDeprecatedButton).trigger('click');
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('showForkMessage')).toBeTruthy();
+ it('does not have tooltip', () => {
+ expect(getTooltip()).toEqual({ id: TOOLTIP_ID, title: 'Edit file in...' });
});
- });
- it('doesnt emit a show fork message event if current user cannot fork', () => {
- createComponent({
- editPath,
- canCurrentUserFork: false,
+ it('shows pencil dropdown', () => {
+ expect(wrapper.find(GlIcon).props('name')).toBe('pencil');
+ expect(wrapper.find('.gl-dropdown-caret').exists()).toBe(true);
});
- wrapper.find(GlDeprecatedButton).trigger('click');
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('showForkMessage')).toBeFalsy();
+ describe.each`
+ event | expectedEmit | expectedRootEmit
+ ${'show'} | ${'open'} | ${[['bv::hide::tooltip', TOOLTIP_ID]]}
+ ${'hide'} | ${'close'} | ${[]}
+ `('when dropdown emits $event', ({ event, expectedEmit, expectedRootEmit }) => {
+ let rootEmitSpy;
+
+ beforeEach(() => {
+ rootEmitSpy = jest.spyOn(wrapper.vm.$root, '$emit');
+
+ findDropdown().vm.$emit(event);
+ });
+
+ it(`emits ${expectedEmit}`, () => {
+ expect(wrapper.emitted(expectedEmit)).toEqual([[]]);
+ });
+
+ it(`emits root = ${JSON.stringify(expectedRootEmit)}`, () => {
+ expect(rootEmitSpy.mock.calls).toEqual(expectedRootEmit);
+ });
});
});
- it('doesnt emit a show fork message event if current user can modify blob', () => {
- createComponent({
- editPath,
- canCurrentUserFork: true,
- canModifyBlob: true,
+ describe('with cant modify blob and can fork', () => {
+ beforeEach(() => {
+ createComponent({
+ canModifyBlob: false,
+ canCurrentUserFork: true,
+ });
});
- wrapper.find(GlDeprecatedButton).trigger('click');
- return wrapper.vm.$nextTick().then(() => {
- expect(wrapper.emitted('showForkMessage')).toBeFalsy();
+ it('when try to open, emits showForkMessage', () => {
+ expect(wrapper.emitted('showForkMessage')).toBeUndefined();
+
+ const event = triggerShow();
+
+ expect(wrapper.emitted('showForkMessage')).toEqual([[]]);
+ expect(event.preventDefault).toHaveBeenCalled();
+ expect(wrapper.emitted('open')).toBeUndefined();
});
});
- it('disables button if editPath is empty', () => {
- createComponent({
- editPath: '',
- canCurrentUserFork: true,
- canModifyBlob: true,
+ describe('with editPath is falsey', () => {
+ beforeEach(() => {
+ createComponent({
+ editPath: '',
+ });
+ });
+
+ it('should disable dropdown', () => {
+ expect(findDropdown().attributes('disabled')).toBe('true');
});
- expect(wrapper.find(GlDeprecatedButton).attributes('disabled')).toBe('true');
+ it('should have tooltip', () => {
+ expect(getTooltip()).toEqual({
+ id: TOOLTIP_ID,
+ title: "Can't edit as source branch was deleted",
+ });
+ });
});
});
diff --git a/spec/frontend/grafana_integration/components/__snapshots__/grafana_integration_spec.js.snap b/spec/frontend/grafana_integration/components/__snapshots__/grafana_integration_spec.js.snap
index 0befe1aa192..dd889e2ab6f 100644
--- a/spec/frontend/grafana_integration/components/__snapshots__/grafana_integration_spec.js.snap
+++ b/spec/frontend/grafana_integration/components/__snapshots__/grafana_integration_spec.js.snap
@@ -17,6 +17,7 @@ exports[`grafana integration component default state to match the default snapsh
</h3>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-settings-toggle"
icon=""
@@ -96,6 +97,7 @@ exports[`grafana integration component default state to match the default snapsh
class="gl-display-flex gl-justify-content-end"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
icon=""
size="medium"
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
index cab2165b5db..cd8a3d7a381 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/alerts_form_spec.js.snap
@@ -97,6 +97,7 @@ exports[`Alert integration settings form default state should match the default
class="gl-display-flex gl-justify-content-end"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-no-auto-disable"
data-qa-selector="save_changes_button"
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap
index 3ad4c13382d..53c3e131466 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap
@@ -18,6 +18,7 @@ exports[`IncidentsSettingTabs should render the component 1`] = `
</h4>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-settings-toggle"
icon=""
diff --git a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
index 78bb238fcb6..a6c910f9023 100644
--- a/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
+++ b/spec/frontend/incidents_settings/components/__snapshots__/pagerduty_form_spec.js.snap
@@ -46,6 +46,7 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
class="gl-display-flex gl-justify-content-end"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="gl-mt-3"
data-testid="webhook-reset-btn"
@@ -80,6 +81,7 @@ exports[`Alert integration settings form should match the default snapshot 1`] =
class="gl-display-flex gl-justify-content-end"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-no-auto-disable"
icon=""
diff --git a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
index 6aaefed92d0..cf5451490eb 100644
--- a/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages/shared/components/__snapshots__/package_list_row_spec.js.snap
@@ -118,6 +118,7 @@ exports[`packages_list_row renders 1`] = `
>
<gl-button-stub
aria-label="Remove package"
+ buttontextclasses=""
category="primary"
data-testid="action-delete"
icon="remove"
diff --git a/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap b/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap
index 2fbc700d4f5..ddeaa2a79db 100644
--- a/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap
+++ b/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap
@@ -39,6 +39,7 @@ exports[`User Operation confirmation modal renders modal with form included 1`]
/>
</form>
<gl-button-stub
+ buttontextclasses=""
category="primary"
icon=""
size="medium"
@@ -48,6 +49,7 @@ exports[`User Operation confirmation modal renders modal with form included 1`]
</gl-button-stub>
<gl-button-stub
+ buttontextclasses=""
category="primary"
disabled="true"
icon=""
@@ -60,6 +62,7 @@ exports[`User Operation confirmation modal renders modal with form included 1`]
</gl-button-stub>
<gl-button-stub
+ buttontextclasses=""
category="primary"
disabled="true"
icon=""
diff --git a/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap b/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap
index 455467e7b29..a0fd6012546 100644
--- a/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap
+++ b/spec/frontend/projects/components/__snapshots__/project_delete_button_spec.js.snap
@@ -17,6 +17,7 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
/>
<gl-button-stub
+ buttontextclasses=""
category="primary"
icon=""
role="button"
diff --git a/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap b/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap
index 692b8f6cf52..4630415f61c 100644
--- a/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap
+++ b/spec/frontend/projects/components/shared/__snapshots__/delete_button_spec.js.snap
@@ -18,6 +18,7 @@ exports[`Project remove modal intialized matches the snapshot 1`] = `
/>
<gl-button-stub
+ buttontextclasses=""
category="primary"
icon=""
role="button"
@@ -84,6 +85,7 @@ exports[`Project remove modal intialized matches the snapshot 1`] = `
<template>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-modal-action-cancel"
icon=""
@@ -98,6 +100,7 @@ exports[`Project remove modal intialized matches the snapshot 1`] = `
<!---->
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="js-modal-action-primary"
disabled="true"
diff --git a/spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap b/spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap
index 22689080063..6b3d65ff037 100644
--- a/spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap
+++ b/spec/frontend/serverless/components/__snapshots__/empty_state_spec.js.snap
@@ -11,7 +11,7 @@ exports[`EmptyStateComponent should render content 1`] = `
<p>In order to start using functions as a service, you must first install Knative on your Kubernetes cluster. <gl-link-stub href=\\"/help\\">More information</gl-link-stub>
</p>
<div>
- <gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" href=\\"/clusters\\">Install Knative</gl-button-stub>
+ <gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" href=\\"/clusters\\">Install Knative</gl-button-stub>
<!---->
</div>
</div>
diff --git a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
index dfd114a2d1c..ec4a81054db 100644
--- a/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
+++ b/spec/frontend/vue_shared/components/__snapshots__/clone_dropdown_spec.js.snap
@@ -39,6 +39,7 @@ exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
tag="div"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="d-inline-flex"
data-clipboard-text="ssh://foo.bar"
@@ -80,6 +81,7 @@ exports[`Clone Dropdown Button rendering matches the snapshot 1`] = `
tag="div"
>
<gl-button-stub
+ buttontextclasses=""
category="primary"
class="d-inline-flex"
data-clipboard-text="http://foo.bar"
diff --git a/spec/serializers/cluster_serializer_spec.rb b/spec/serializers/cluster_serializer_spec.rb
index 04999975276..e65e97b6ae0 100644
--- a/spec/serializers/cluster_serializer_spec.rb
+++ b/spec/serializers/cluster_serializer_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe ClusterSerializer do
:cluster_type,
:enabled,
:environment_scope,
+ :id,
:gitlab_managed_apps_logs_path,
:enable_advanced_logs_querying,
:kubernetes_errors,
diff --git a/spec/serializers/diff_file_base_entity_spec.rb b/spec/serializers/diff_file_base_entity_spec.rb
index 94c39e11790..99dbaff4b7e 100644
--- a/spec/serializers/diff_file_base_entity_spec.rb
+++ b/spec/serializers/diff_file_base_entity_spec.rb
@@ -3,10 +3,24 @@
require 'spec_helper'
RSpec.describe DiffFileBaseEntity do
- let(:project) { create(:project, :repository) }
+ include ProjectForksHelper
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
let(:repository) { project.repository }
let(:entity) { described_class.new(diff_file, options).as_json }
+ shared_examples 'nil if removed source branch' do |key|
+ before do
+ allow(merge_request).to receive(:source_branch_exists?).and_return(false)
+ end
+
+ specify do
+ expect(entity[key]).to eq(nil)
+ end
+ end
+
context 'submodule information for a' do
let(:commit_sha) { "" }
let(:commit) { project.commit(commit_sha) }
@@ -67,7 +81,7 @@ RSpec.describe DiffFileBaseEntity do
context 'edit_path' do
let(:diff_file) { merge_request.diffs.diff_files.to_a.last }
- let(:options) { { request: EntityRequest.new(current_user: create(:user)), merge_request: merge_request } }
+ let(:options) { { request: EntityRequest.new(current_user: user), merge_request: merge_request } }
let(:params) { {} }
shared_examples 'a diff file edit path to the source branch' do
@@ -81,16 +95,7 @@ RSpec.describe DiffFileBaseEntity do
let(:params) { { from_merge_request_iid: merge_request.iid } }
it_behaves_like 'a diff file edit path to the source branch'
-
- context 'removed source branch' do
- before do
- allow(merge_request).to receive(:source_branch_exists?).and_return(false)
- end
-
- it do
- expect(entity[:edit_path]).to eq(nil)
- end
- end
+ it_behaves_like 'nil if removed source branch', :edit_path
end
context 'closed' do
@@ -118,4 +123,30 @@ RSpec.describe DiffFileBaseEntity do
end
end
end
+
+ context 'ide_edit_path' do
+ let(:source_project) { project }
+ let(:merge_request) { create(:merge_request, iid: 123, target_project: target_project, source_project: source_project) }
+ let(:diff_file) { merge_request.diffs.diff_files.to_a.last }
+ let(:options) { { request: EntityRequest.new(current_user: user), merge_request: merge_request } }
+ let(:expected_merge_request_path) { "/-/ide/project/#{source_project.full_path}/merge_requests/#{merge_request.iid}" }
+
+ context 'when source_project and target_project are the same' do
+ let(:target_project) { source_project }
+
+ it_behaves_like 'nil if removed source branch', :ide_edit_path
+
+ it 'returns the merge_request ide route' do
+ expect(entity[:ide_edit_path]).to eq expected_merge_request_path
+ end
+ end
+
+ context 'when source_project and target_project are different' do
+ let(:target_project) { fork_project(source_project, source_project.owner, repository: true) }
+
+ it 'returns the merge_request ide route with the target_project as param' do
+ expect(entity[:ide_edit_path]).to eq("#{expected_merge_request_path}?target_project=#{ERB::Util.url_encode(target_project.full_path)}")
+ end
+ end
+ end
end
diff --git a/yarn.lock b/yarn.lock
index 9bb1756daa6..d9967c03d4e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -848,10 +848,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.164.0.tgz#6cefad871c45f945ef92b99015d0f510b1d2de4a"
integrity sha512-a9e/cYUc1QQk7azjH4x/m6/p3icavwGEi5F9ipNlDqiJtUor5tqojxvMxPOhuVbN/mTwnC6lGsSZg4tqTsdJAQ==
-"@gitlab/ui@21.3.1":
- version "21.3.1"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.3.1.tgz#027b767804540539da73d4874370895d7398adea"
- integrity sha512-ynyg8i8W8Ud+GoySr4hAjJoW55kWMwSEFLX5MEX8CbdqGurkTLqHYLLpXPBSSnVEcw4stR+bFbKSc35rmBkWPA==
+"@gitlab/ui@21.4.2":
+ version "21.4.2"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.4.2.tgz#c3d36167ab4df49ce978e20bdd3790e716f5a2d1"
+ integrity sha512-p8ujeGvCG06Opn0eQlrwZyi9v9RK3T2V4TUcljTAUYDdm0p23qJjjIlFjfGHlQsNg0wRgnkbKFXfkZ/Oy8GyiQ==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"