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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-01-23 21:11:12 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-01-23 21:11:12 +0300
commitcfc8827f6bf9573b02401b1908728da3aed96698 (patch)
tree30180d04062db3e56d1cc3772888ff4f15e56c10
parenta8b96c3072b3bd4d45e6364931042b350bf7fa2e (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/integrations/constants.js2
-rw-r--r--app/assets/javascripts/integrations/index/components/integrations_table.vue14
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/constants.js8
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue9
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_tags_popover.vue42
-rw-r--r--app/assets/javascripts/super_sidebar/components/create_menu.vue34
-rw-r--r--app/assets/javascripts/super_sidebar/components/super_sidebar.vue1
-rw-r--r--app/assets/javascripts/super_sidebar/components/user_bar.vue9
-rw-r--r--app/graphql/types/ci/job_type.rb2
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/helpers/nav/new_dropdown_helper.rb6
-rw-r--r--app/helpers/sidebars_helper.rb23
-rw-r--r--app/views/admin/application_settings/_help_page.html.haml2
-rw-r--r--app/views/admin/background_migrations/_migration.html.haml21
-rw-r--r--app/views/layouts/_page.html.haml2
-rw-r--r--doc/administration/geo/secondary_proxy/location_aware_external_url.md2
-rw-r--r--doc/administration/logs/log_parsing.md2
-rw-r--r--doc/administration/operations/rails_console.md8
-rw-r--r--doc/administration/packages/container_registry.md4
-rw-r--r--doc/administration/pages/index.md5
-rw-r--r--doc/administration/postgresql/moving.md4
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/api/groups.md3
-rw-r--r--doc/api/jobs.md11
-rw-r--r--doc/api/members.md3
-rw-r--r--doc/ci/directed_acyclic_graph/index.md4
-rw-r--r--doc/ci/pipelines/index.md4
-rw-r--r--doc/development/documentation/workflow.md2
-rw-r--r--doc/integration/kerberos.md2
-rw-r--r--doc/topics/git/getting_started.md95
-rw-r--r--doc/user/admin_area/appearance.md2
-rw-r--r--doc/user/enterprise_user/index.md71
-rw-r--r--doc/user/group/manage.md2
-rw-r--r--doc/user/group/saml_sso/index.md4
-rw-r--r--doc/user/group/saml_sso/scim_setup.md2
-rw-r--r--doc/user/project/badges.md4
-rw-r--r--doc/user/project/code_owners.md23
-rw-r--r--doc/user/project/merge_requests/approvals/index.md2
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/index.md53
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md56
-rw-r--r--doc/user/project/pages/getting_started/pages_ci_cd_template.md6
-rw-r--r--doc/user/project/pages/getting_started/pages_forked_sample_project.md7
-rw-r--r--doc/user/project/pages/getting_started/pages_from_scratch.md7
-rw-r--r--doc/user/project/pages/getting_started/pages_new_project_template.md5
-rw-r--r--doc/user/project/pages/getting_started/pages_ui.md10
-rw-r--r--doc/user/project/pages/introduction.md3
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md41
-rw-r--r--doc/user/project/repository/web_editor.md24
-rw-r--r--doc/user/project/service_desk.md2
-rw-r--r--doc/user/project/web_ide/index.md6
-rw-r--r--lib/api/entities/ci/job_basic.rb1
-rw-r--r--locale/gitlab.pot18
-rw-r--r--package.json4
-rw-r--r--qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb23
-rw-r--r--spec/features/admin/admin_sees_background_migrations_spec.rb8
-rw-r--r--spec/features/admin/admin_settings_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_slack_notifications_spec.rb1
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/job.json2
-rw-r--r--spec/frontend/admin/broadcast_messages/components/base_spec.js5
-rw-r--r--spec/frontend/feature_flags/store/edit/actions_spec.js7
-rw-r--r--spec/frontend/feature_flags/store/new/actions_spec.js5
-rw-r--r--spec/frontend/gpg_badges_spec.js11
-rw-r--r--spec/frontend/groups/components/invite_members_banner_spec.js5
-rw-r--r--spec/frontend/integrations/index/components/integrations_table_spec.js57
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_tags_popover_spec.js48
-rw-r--r--spec/frontend/lib/utils/apollo_startup_js_link_spec.js10
-rw-r--r--spec/frontend/lib/utils/axios_startup_calls_spec.js16
-rw-r--r--spec/frontend/lib/utils/axios_utils_spec.js4
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap3
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap7
-rw-r--r--spec/frontend/notes/stores/actions_spec.js29
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap1
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js4
-rw-r--r--spec/frontend/persistent_user_callout_spec.js9
-rw-r--r--spec/frontend/sidebar/sidebar_mediator_spec.js11
-rw-r--r--spec/frontend/super_sidebar/components/counter_spec.js4
-rw-r--r--spec/frontend/super_sidebar/components/create_menu_spec.js33
-rw-r--r--spec/frontend/super_sidebar/components/super_sidebar_spec.js4
-rw-r--r--spec/frontend/super_sidebar/components/user_bar_spec.js10
-rw-r--r--spec/frontend/super_sidebar/mock_data.js42
-rw-r--r--spec/frontend/vue_shared/components/dismissible_container_spec.js3
-rw-r--r--spec/graphql/types/ci/job_type_spec.rb1
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/helpers/nav/new_dropdown_helper_spec.rb20
-rw-r--r--spec/helpers/sidebars_helper_spec.rb47
-rw-r--r--spec/requests/api/ci/jobs_spec.rb13
-rw-r--r--spec/support/shared_examples/integrations/integration_settings_form.rb1
-rw-r--r--spec/tasks/gitlab/db_rake_spec.rb90
-rw-r--r--yarn.lock18
89 files changed, 842 insertions, 396 deletions
diff --git a/app/assets/javascripts/integrations/constants.js b/app/assets/javascripts/integrations/constants.js
index b956bdf067d..bd7c38e96fe 100644
--- a/app/assets/javascripts/integrations/constants.js
+++ b/app/assets/javascripts/integrations/constants.js
@@ -88,7 +88,7 @@ export const billingPlanNames = {
[billingPlans.ULTIMATE]: s__('BillingPlans|Ultimate'),
};
-const INTEGRATION_TYPE_SLACK = 'slack';
+export const INTEGRATION_TYPE_SLACK = 'slack';
const INTEGRATION_TYPE_SLACK_APPLICATION = 'gitlab_slack_application';
const INTEGRATION_TYPE_MATTERMOST = 'mattermost';
diff --git a/app/assets/javascripts/integrations/index/components/integrations_table.vue b/app/assets/javascripts/integrations/index/components/integrations_table.vue
index 439c243f418..62f0fe4d6bf 100644
--- a/app/assets/javascripts/integrations/index/components/integrations_table.vue
+++ b/app/assets/javascripts/integrations/index/components/integrations_table.vue
@@ -1,7 +1,9 @@
<script>
import { GlIcon, GlLink, GlTable, GlTooltipDirective } from '@gitlab/ui';
+import { INTEGRATION_TYPE_SLACK } from '~/integrations/constants';
import { sprintf, s__, __ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
@@ -13,6 +15,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
integrations: {
type: Array,
@@ -55,6 +58,15 @@ export default {
},
];
},
+ filteredIntegrations() {
+ if (this.glFeatures.integrationSlackAppNotifications) {
+ return this.integrations.filter(
+ (integration) =>
+ !(integration.name === INTEGRATION_TYPE_SLACK && integration.active === false),
+ );
+ }
+ return this.integrations;
+ },
},
methods: {
getStatusTooltipTitle(integration) {
@@ -67,7 +79,7 @@ export default {
</script>
<template>
- <gl-table :items="integrations" :fields="fields" :empty-text="emptyText" show-empty fixed>
+ <gl-table :items="filteredIntegrations" :fields="fields" :empty-text="emptyText" show-empty fixed>
<template #cell(active)="{ item }">
<gl-icon
v-if="item.active"
diff --git a/app/assets/javascripts/issues/show/components/incidents/constants.js b/app/assets/javascripts/issues/show/components/incidents/constants.js
index 2fdae538902..eeebd6bb32b 100644
--- a/app/assets/javascripts/issues/show/components/incidents/constants.js
+++ b/app/assets/javascripts/issues/show/components/incidents/constants.js
@@ -50,6 +50,14 @@ export const timelineEventTagsI18n = Object.freeze({
endTime: __('End time'),
});
+export const timelineEventTagsPopover = Object.freeze({
+ title: s__('Incident|Event tag'),
+ message: s__(
+ 'Incident|Adding an event tag associates the timeline comment with specific incident metrics.',
+ ),
+ link: __('Learn more'),
+});
+
export const MAX_TEXT_LENGTH = 280;
export const TIMELINE_EVENT_TAGS = Object.values(timelineEventTagsI18n).map((item) => ({
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
index 2fa1308d7d8..7f98e74f4aa 100644
--- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue
@@ -3,6 +3,7 @@ import { GlDatepicker, GlFormInput, GlFormGroup, GlButton, GlCollapsibleListbox
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __, sprintf } from '~/locale';
+import TimelineEventsTagsPopover from './timeline_events_tags_popover.vue';
import { MAX_TEXT_LENGTH, TIMELINE_EVENT_TAGS, timelineFormI18n } from './constants';
import { getUtcShiftedDate, getPreviousEventTags } from './utils';
@@ -21,6 +22,7 @@ export default {
],
components: {
MarkdownField,
+ TimelineEventsTagsPopover,
GlDatepicker,
GlFormInput,
GlFormGroup,
@@ -197,8 +199,13 @@ export default {
<p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p>
</div>
</div>
- <gl-form-group v-if="glFeatures.incidentEventTags" :label="$options.i18n.tagsLabel">
+ <gl-form-group v-if="glFeatures.incidentEventTags">
+ <label class="gl-display-flex gl-align-items-center gl-gap-3" for="timeline-input-tags">
+ {{ $options.i18n.tagsLabel }}
+ <timeline-events-tags-popover />
+ </label>
<gl-collapsible-listbox
+ id="timeline-input-tags"
:selected="selectedTags"
:toggle-text="listboxText"
:items="tags"
diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tags_popover.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tags_popover.vue
new file mode 100644
index 00000000000..772a16e9ba2
--- /dev/null
+++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tags_popover.vue
@@ -0,0 +1,42 @@
+<script>
+import { GlIcon, GlPopover, GlLink } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import { timelineEventTagsPopover } from './constants';
+
+export default {
+ name: 'TimelineEventsTagsPopover',
+ components: {
+ GlIcon,
+ GlPopover,
+ GlLink,
+ },
+ i18n: timelineEventTagsPopover,
+ learnMoreLink: helpPagePath('ee/operations/incident_management/incident_timeline_events', {
+ anchor: 'incident-tags',
+ }),
+};
+</script>
+
+<template>
+ <span>
+ <gl-icon id="timeline-events-tag-question" name="question-o" class="gl-text-blue-600" />
+
+ <gl-popover
+ target="timeline-events-tag-question"
+ triggers="hover focus"
+ placement="top"
+ container="viewport"
+ :title="$options.i18n.title"
+ >
+ <div>
+ <p class="gl-mb-0">
+ {{ $options.i18n.message }}
+ </p>
+ <gl-link target="_blank" class="gl-font-sm" :href="$options.learnMoreLink">{{
+ $options.i18n.link
+ }}</gl-link
+ >.
+ </div>
+ </gl-popover>
+ </span>
+</template>
diff --git a/app/assets/javascripts/super_sidebar/components/create_menu.vue b/app/assets/javascripts/super_sidebar/components/create_menu.vue
new file mode 100644
index 00000000000..2d2eef19473
--- /dev/null
+++ b/app/assets/javascripts/super_sidebar/components/create_menu.vue
@@ -0,0 +1,34 @@
+<script>
+import { GlDisclosureDropdown, GlTooltipDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ components: {
+ GlDisclosureDropdown,
+ },
+ i18n: {
+ createNew: __('Create new...'),
+ },
+ props: {
+ groups: {
+ type: Array,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-disclosure-dropdown
+ v-gl-tooltip:super-sidebar.bottom="$options.i18n.createNew"
+ category="tertiary"
+ icon="plus"
+ :items="groups"
+ no-caret
+ text-sr-only
+ :toggle-text="$options.i18n.createNew"
+ />
+</template>
diff --git a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue
index e2eac64f5ad..78d761e9a59 100644
--- a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue
+++ b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue
@@ -31,6 +31,7 @@ export default {
<template>
<aside
+ id="super-sidebar"
class="super-sidebar gl-fixed gl-bottom-0 gl-left-0 gl-display-flex gl-flex-direction-column gl-bg-gray-10 gl-border-r gl-border-gray-a-08 gl-z-index-9999"
data-testid="super-sidebar"
>
diff --git a/app/assets/javascripts/super_sidebar/components/user_bar.vue b/app/assets/javascripts/super_sidebar/components/user_bar.vue
index 7ee1776bf07..22ef58eb302 100644
--- a/app/assets/javascripts/super_sidebar/components/user_bar.vue
+++ b/app/assets/javascripts/super_sidebar/components/user_bar.vue
@@ -4,6 +4,7 @@ import { __ } from '~/locale';
import SafeHtml from '~/vue_shared/directives/safe_html';
import NewNavToggle from '~/nav/components/new_nav_toggle.vue';
import logo from '../../../../views/shared/_logo.svg';
+import CreateMenu from './create_menu.vue';
import Counter from './counter.vue';
export default {
@@ -12,10 +13,12 @@ export default {
GlAvatar,
GlDropdown,
GlIcon,
+ CreateMenu,
NewNavToggle,
Counter,
},
i18n: {
+ createNew: __('Create new...'),
issues: __('Issues'),
mergeRequests: __('Merge requests'),
todoList: __('To-Do list'),
@@ -39,11 +42,7 @@ export default {
<div class="gl-flex-grow-1">
<a v-safe-html="$options.logo" :href="rootPath"></a>
</div>
- <gl-dropdown variant="link" no-caret>
- <template #button-content>
- <gl-icon name="plus" class="gl-vertical-align-middle gl-text-black-normal" />
- </template>
- </gl-dropdown>
+ <create-menu :groups="sidebarData.create_new_menu_groups" />
<button class="gl-border-none">
<gl-icon name="search" class="gl-vertical-align-middle" />
</button>
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index 4c466f97d61..a97e9cee4b1 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -37,6 +37,8 @@ module Types
# Life-cycle timestamps:
field :created_at, Types::TimeType, null: false,
description: "When the job was created."
+ field :erased_at, Types::TimeType, null: true,
+ description: "When the job was erased."
field :finished_at, Types::TimeType, null: true,
description: 'When a job has finished running.'
field :queued_at, Types::TimeType, null: true,
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index f4b34044e0e..ef174584a4b 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -232,7 +232,7 @@ module ApplicationHelper
end
def support_url
- Gitlab::CurrentSettings.current_application_settings.help_page_support_url.presence || "#{promo_url}/getting-help/"
+ Gitlab::CurrentSettings.current_application_settings.help_page_support_url.presence || "#{promo_url}/get-help/"
end
def instance_review_permitted?
diff --git a/app/helpers/nav/new_dropdown_helper.rb b/app/helpers/nav/new_dropdown_helper.rb
index b017c9a81d1..4972d359bd6 100644
--- a/app/helpers/nav/new_dropdown_helper.rb
+++ b/app/helpers/nav/new_dropdown_helper.rb
@@ -59,7 +59,7 @@ module Nav
end
{
- title: _('This group'),
+ title: _('In this group'),
menu_items: menu_items.compact
}
end
@@ -110,7 +110,7 @@ module Nav
end
{
- title: _('This project'),
+ title: _('In this project'),
menu_items: menu_items
}
end
@@ -152,7 +152,7 @@ module Nav
end
{
- title: _('GitLab'),
+ title: _('In GitLab'),
menu_items: menu_items
}
end
diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb
index f6257c92f3f..9ce68df26a1 100644
--- a/app/helpers/sidebars_helper.rb
+++ b/app/helpers/sidebars_helper.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
module SidebarsHelper
+ include Nav::NewDropdownHelper
+
def sidebar_tracking_attributes_by_object(object)
sidebar_attributes_for_object(object).fetch(:tracking_attrs, {})
end
@@ -31,7 +33,7 @@ module SidebarsHelper
Sidebars::Groups::Context.new(**context_data)
end
- def super_sidebar_context(user)
+ def super_sidebar_context(user, group:, project:)
{
name: user.name,
username: user.username,
@@ -39,12 +41,29 @@ module SidebarsHelper
assigned_open_issues_count: user.assigned_open_issues_count,
assigned_open_merge_requests_count: user.assigned_open_merge_requests_count,
todos_pending_count: user.todos_pending_count,
- issues_dashboard_path: issues_dashboard_path(assignee_username: user.username)
+ issues_dashboard_path: issues_dashboard_path(assignee_username: user.username),
+ create_new_menu_groups: create_new_menu_groups(group: group, project: project)
}
end
private
+ def create_new_menu_groups(group:, project:)
+ new_dropdown_sections = new_dropdown_view_model(group: group, project: project)[:menu_sections]
+ show_headers = new_dropdown_sections.length > 1
+ new_dropdown_sections.map do |section|
+ {
+ name: show_headers ? section[:title] : '',
+ items: section[:menu_items].map do |item|
+ {
+ text: item[:title],
+ href: item[:href]
+ }
+ end
+ }
+ end
+ end
+
def sidebar_attributes_for_object(object)
case object
when Project
diff --git a/app/views/admin/application_settings/_help_page.html.haml b/app/views/admin/application_settings/_help_page.html.haml
index 11ebad07e9a..e76a83662af 100644
--- a/app/views/admin/application_settings/_help_page.html.haml
+++ b/app/views/admin/application_settings/_help_page.html.haml
@@ -12,7 +12,7 @@
= f.gitlab_ui_checkbox_component :help_page_hide_commercial_content, _('Hide marketing-related entries from the Help page')
.form-group
= f.label :help_page_support_url, _('Support page URL'), class: 'label-bold'
- = f.text_field :help_page_support_url, class: 'form-control gl-form-input', placeholder: 'https://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
+ = f.text_field :help_page_support_url, class: 'form-control gl-form-input', placeholder: 'https://company.example.com/get-help', :'aria-describedby' => 'support_help_block'
%span.form-text.text-muted#support_help_block= _('Alternate support URL for Help page and Help dropdown.')
.form-group
diff --git a/app/views/admin/background_migrations/_migration.html.haml b/app/views/admin/background_migrations/_migration.html.haml
index f4906028e39..99cb63709f5 100644
--- a/app/views/admin/background_migrations/_migration.html.haml
+++ b/app/views/admin/background_migrations/_migration.html.haml
@@ -12,14 +12,17 @@
= gl_badge_tag migration.status_name.to_s.humanize, { size: :sm, variant: batched_migration_status_badge_variant(migration) }
%td{ role: 'cell', data: { label: _('Action') } }
- if migration.active?
- = button_to pause_admin_background_migration_path(migration, database: params[:database]),
- class: 'gl-button btn btn-icon has-tooltip', title: _('Pause'), 'aria-label' => _('Pause') do
- = sprite_icon('pause', css_class: 'gl-button-icon gl-icon')
+ = render Pajamas::ButtonComponent.new(icon: 'pause',
+ method: :post,
+ href: pause_admin_background_migration_path(migration, database: params[:database]),
+ button_options: { class: 'has-tooltip', title: _('Pause'), 'aria-label' => _('Pause') })
- elsif migration.paused?
- = button_to resume_admin_background_migration_path(migration, database: params[:database]),
- class: 'gl-button btn btn-icon has-tooltip', title: _('Resume'), 'aria-label' => _('Resume') do
- = sprite_icon('play', css_class: 'gl-button-icon gl-icon')
+ = render Pajamas::ButtonComponent.new(icon: 'play',
+ method: :post,
+ href: resume_admin_background_migration_path(migration, database: params[:database]),
+ button_options: { class: 'has-tooltip', title: _('Resume'), 'aria-label' => _('Resume') })
- elsif migration.failed?
- = button_to retry_admin_background_migration_path(migration, database: params[:database]),
- class: 'gl-button btn btn-icon has-tooltip', title: _('Retry'), 'aria-label' => _('Retry') do
- = sprite_icon('retry', css_class: 'gl-button-icon gl-icon')
+ = render Pajamas::ButtonComponent.new(icon: 'retry',
+ method: :post,
+ href: retry_admin_background_migration_path(migration, database: params[:database]),
+ button_options: { class: 'has-tooltip', title: _('Retry'), 'aria-label' => _('Retry') })
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 010ddd8da39..8e39ef7301d 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -2,7 +2,7 @@
- @left_sidebar = true
.layout-page.hide-when-top-nav-responsive-open{ class: page_with_sidebar_class }
- if show_super_sidebar?
- - sidebar_data = super_sidebar_context(current_user).to_json
+ - sidebar_data = super_sidebar_context(current_user, group: @group, project: @project).to_json
%aside.js-super-sidebar.nav-sidebar{ data: { root_path: root_path, sidebar: sidebar_data, toggle_new_nav_endpoint: profile_preferences_url } }
- elsif defined?(nav) && nav
= render "layouts/nav/sidebar/#{nav}"
diff --git a/doc/administration/geo/secondary_proxy/location_aware_external_url.md b/doc/administration/geo/secondary_proxy/location_aware_external_url.md
index b71b813ee9f..ae701bb8482 100644
--- a/doc/administration/geo/secondary_proxy/location_aware_external_url.md
+++ b/doc/administration/geo/secondary_proxy/location_aware_external_url.md
@@ -86,7 +86,7 @@ When creating Geo-Based record sets, GCP applies a nearest match for the source
1. Select **Network Services** > **Cloud DNS**.
1. Select the Zone configured for your domain.
1. Select **Add Record Set**.
-1. Enter the DNS Name for your Location-aware public URL e.g. `gitlab.example.com`.
+1. Enter the DNS Name for your Location-aware public URL, for example, `gitlab.example.com`.
1. Select the **Routing Policy**: **Geo-Based**.
1. Select **Add Managed RRData**.
1. Select **Source Region**: **us-central1**.
diff --git a/doc/administration/logs/log_parsing.md b/doc/administration/logs/log_parsing.md
index dfe434ed1f2..0439d4967cb 100644
--- a/doc/administration/logs/log_parsing.md
+++ b/doc/administration/logs/log_parsing.md
@@ -166,7 +166,7 @@ jq --raw-output '[.route, .ua] | @tsv' api_json.log | sort | uniq -c | sort -n
1234 /api/:version/internal/allowed GitLab-Shell
```
-This sample response seems normal. A custom tool or script might be causing a high load
+This sample response seems typical. A custom tool or script might be causing a high load
if the output contains many:
- Third party libraries like `python-requests` or `curl`.
diff --git a/doc/administration/operations/rails_console.md b/doc/administration/operations/rails_console.md
index f2143435755..652a4fa5497 100644
--- a/doc/administration/operations/rails_console.md
+++ b/doc/administration/operations/rails_console.md
@@ -158,7 +158,7 @@ sudo -u git -H bundle exec rails runner -e production /path/to/script.rb
Rails Runner does not produce the same output as the console.
-If you set a variable on the console, the console will generate useful debug output
+If you set a variable on the console, the console generates useful debug output
such as the variable contents or properties of referenced entity:
```ruby
@@ -176,7 +176,7 @@ root
1
```
-Some basic knowledge of Ruby will be very useful. Try
+Some basic knowledge of Ruby is very useful. Try
[this 30-minute tutorial](https://try.ruby-lang.org/) for a quick introduction.
Rails experience is helpful but not essential.
@@ -425,7 +425,7 @@ D, [2020-03-05T17:18:30.406047 #910] DEBUG -- : User Load (2.6ms) SELECT "use
```
For more on different ways to retrieve data from the database using Active
-Record, please see the [Active Record Query Interface documentation](https://guides.rubyonrails.org/active_record_querying.html).
+Record, see the [Active Record Query Interface documentation](https://guides.rubyonrails.org/active_record_querying.html).
## Query the database using an Active Record model
@@ -547,7 +547,7 @@ be the fastest way to get to the root of the problem.
### Interacting with Active Record objects
-At the end of the day, Active Record objects are just normal Ruby objects. As
+At the end of the day, Active Record objects are just standard Ruby objects. As
such, we can define methods on them which perform arbitrary actions.
For example, GitLab developers have added some methods which help with
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 6446252e143..a1c31c188b9 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -1136,7 +1136,7 @@ To enable the read-only mode:
1. Next, trigger one of the garbage collect commands:
WARNING:
- You must use `/opt/gitlab/embedded/bin/registry` to recycle unused tags. If you use `gitlab-ctl registry-garbage-collect`, you **will bring the container registry down**.
+ You must use `/opt/gitlab/embedded/bin/registry` to recycle unused tags. If you use `gitlab-ctl registry-garbage-collect`, **the container registry goes down**.
```shell
# Recycling unused tags
@@ -1706,7 +1706,7 @@ case, since we know that since the login succeeded, we probably need to look
at the communication between the client and the Registry.
The REST API between the Docker client and Registry is described
-[in the Docker documentation](https://docs.docker.com/registry/spec/api/). Normally, one would just
+[in the Docker documentation](https://docs.docker.com/registry/spec/api/). Usually, one would just
use Wireshark or tcpdump to capture the traffic and see where things went
wrong. However, since all communications between Docker clients and servers
are done over HTTPS, it's a bit difficult to decrypt the traffic quickly even
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 58003758c8a..c01ee081aee 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -719,7 +719,10 @@ To set the maximum size of GitLab Pages site in a project, overriding the inheri
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Pages**.
-1. Enter a value under **Maximum size of pages** in MB.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../../user/project/pages/index.md#menu-position-test).
+1. In **Maximum size of pages**, enter the size in MB.
1. Select **Save changes**.
## Set maximum number of GitLab Pages custom domains for a project
diff --git a/doc/administration/postgresql/moving.md b/doc/administration/postgresql/moving.md
index 96076205281..7c18297a175 100644
--- a/doc/administration/postgresql/moving.md
+++ b/doc/administration/postgresql/moving.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Sometimes it is necessary to move your databases from one PostgreSQL instance to
another. For example, if you are using AWS Aurora and are preparing to
-enable Database Load Balancing, you will need to move your databases to
+enable Database Load Balancing, you need to move your databases to
RDS for PostgreSQL.
To move databases from one instance to another:
@@ -36,7 +36,7 @@ To move databases from one instance to another:
/opt/gitlab/embedded/bin/pg_dump -h $SRC_PGHOST -U $SRC_PGUSER -c -C -f praefect_production.sql praefect_production
```
-1. Restore the databases to the destination (this will overwrite any existing databases with the same names):
+1. Restore the databases to the destination (this overwrites any existing databases with the same names):
```shell
/opt/gitlab/embedded/bin/psql -h $DST_PGHOST -U $DST_PGUSER -f praefect_production.sql postgres
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index a765333d26b..60b7577df0d 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -11233,6 +11233,7 @@ CI/CD variables for a GitLab instance.
| <a id="cijobdetailedstatus"></a>`detailedStatus` | [`DetailedStatus`](#detailedstatus) | Detailed status of the job. |
| <a id="cijobdownstreampipeline"></a>`downstreamPipeline` | [`Pipeline`](#pipeline) | Downstream pipeline for a bridge. |
| <a id="cijobduration"></a>`duration` | [`Int`](#int) | Duration of the job in seconds. |
+| <a id="cijoberasedat"></a>`erasedAt` | [`Time`](#time) | When the job was erased. |
| <a id="cijobfinishedat"></a>`finishedAt` | [`Time`](#time) | When a job has finished running. |
| <a id="cijobid"></a>`id` | [`JobID`](#jobid) | ID of the job. |
| <a id="cijobkind"></a>`kind` | [`CiJobKind!`](#cijobkind) | Indicates the type of job. |
diff --git a/doc/api/groups.md b/doc/api/groups.md
index c39b8584e93..dd3f8baf338 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -1201,8 +1201,7 @@ GET /groups?search=foobar
> Introduced in GitLab 14.8.
-Get a list of users provisioned by a given group. Does not include subgroups.
-Users in this list are considered [enterprise users](../user/enterprise_user/index.md).
+Get a list of users provisioned by a given group. Does not include users provisioned by subgroups.
Requires at least the Maintainer role on the group.
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index ab8387790d0..64516f0c86b 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -44,6 +44,7 @@ Example of response
"created_at": "2015-12-24T15:51:21.802Z",
"started_at": "2015-12-24T17:54:27.722Z",
"finished_at": "2015-12-24T17:54:27.895Z",
+ "erased_at": null,
"duration": 0.173,
"queued_duration": 0.010,
"artifacts_file": {
@@ -112,6 +113,7 @@ Example of response
"created_at": "2015-12-24T15:51:21.727Z",
"started_at": "2015-12-24T17:54:24.729Z",
"finished_at": "2015-12-24T17:54:24.921Z",
+ "erased_at": null,
"duration": 0.192,
"queued_duration": 0.023,
"artifacts_expire_at": "2016-01-23T17:54:24.921Z",
@@ -199,6 +201,7 @@ Example of response
"created_at": "2015-12-24T15:51:21.727Z",
"started_at": "2015-12-24T17:54:24.729Z",
"finished_at": "2015-12-24T17:54:24.921Z",
+ "erased_at": null,
"duration": 0.192,
"queued_duration": 0.023,
"artifacts_expire_at": "2016-01-23T17:54:24.921Z",
@@ -258,6 +261,7 @@ Example of response
"created_at": "2015-12-24T15:51:21.802Z",
"started_at": "2015-12-24T17:54:27.722Z",
"finished_at": "2015-12-24T17:54:27.895Z",
+ "erased_at": null,
"duration": 0.173,
"queued_duration": 0.023,
"artifacts_file": {
@@ -361,6 +365,7 @@ Example of response
"created_at": "2015-12-24T15:51:21.802Z",
"started_at": "2015-12-24T17:54:27.722Z",
"finished_at": "2015-12-24T17:58:27.895Z",
+ "erased_at": null,
"duration": 240,
"queued_duration": 0.123,
"id": 7,
@@ -449,6 +454,7 @@ Example of response
"created_at": "2015-12-24T15:51:21.880Z",
"started_at": "2015-12-24T17:54:30.733Z",
"finished_at": "2015-12-24T17:54:31.198Z",
+ "erased_at": null,
"duration": 0.465,
"queued_duration": 0.123,
"artifacts_expire_at": "2016-01-23T17:54:31.198Z",
@@ -600,6 +606,7 @@ Example of response
"created_at": "2015-12-24T15:51:21.880Z",
"started_at": "2015-12-24T17:54:30.733Z",
"finished_at": "2015-12-24T17:54:31.198Z",
+ "erased_at": null,
"duration": 0.465,
"queued_duration": 0.010,
"artifacts_expire_at": "2016-01-23T17:54:31.198Z",
@@ -704,6 +711,7 @@ Example of response
"created_at": "2016-01-11T10:13:33.506Z",
"started_at": "2016-01-11T10:14:09.526Z",
"finished_at": null,
+ "erased_at": null,
"duration": 8,
"queued_duration": 0.010,
"id": 1,
@@ -757,6 +765,7 @@ Example of response
"created_at": "2016-01-11T10:13:33.506Z",
"started_at": null,
"finished_at": null,
+ "erased_at": null,
"duration": null,
"queued_duration": 0.010,
"id": 1,
@@ -821,6 +830,7 @@ Example of response
"created_at": "2016-01-11T10:13:33.506Z",
"started_at": "2016-01-11T10:13:33.506Z",
"finished_at": "2016-01-11T10:15:10.506Z",
+ "erased_at": "2016-01-11T11:30:19.914Z",
"duration": 97.0,
"queued_duration": 0.010,
"status": "failed",
@@ -894,6 +904,7 @@ Example response:
"created_at": "2016-01-11T10:13:33.506Z",
"started_at": null,
"finished_at": null,
+ "erased_at": null,
"duration": null,
"queued_duration": 0.010,
"id": 1,
diff --git a/doc/api/members.md b/doc/api/members.md
index 4032ab1d651..2da84866b92 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -29,7 +29,8 @@ In GitLab 14.8 and earlier, projects in personal namespaces have an `access_leve
The `group_saml_identity` attribute is only visible to a group owner for [SSO enabled groups](../user/group/saml_sso/index.md).
-The `email` attribute is only visible to group Owners for any [enterprise user](../user/enterprise_user/index.md).
+The `email` attribute is only visible to group owners when the user was provisioned by the group.
+Users are provisioned by the group when the account was created via [SCIM](../user/group/saml_sso/scim_setup.md) or by first sign-in with [SAML SSO for GitLab.com groups](../user/group/saml_sso/index.md).
## List all members of a group or project
diff --git a/doc/ci/directed_acyclic_graph/index.md b/doc/ci/directed_acyclic_graph/index.md
index 49bce75e183..7c741ef3842 100644
--- a/doc/ci/directed_acyclic_graph/index.md
+++ b/doc/ci/directed_acyclic_graph/index.md
@@ -46,7 +46,7 @@ It has a pipeline that looks like the following:
Using a DAG, you can relate the `_a` jobs to each other separately from the `_b` jobs,
and even if service `a` takes a very long time to build, service `b` doesn't
wait for it and finishes as quickly as it can. In this very same pipeline, `_c` and
-`_d` can be left alone and run together in staged sequence just like any normal
+`_d` can be left alone and run together in staged sequence just like any standard
GitLab pipeline.
## Use cases
@@ -68,7 +68,7 @@ as quickly as possible.
Relationships are defined between jobs using the [`needs` keyword](../yaml/index.md#needs).
-Note that `needs` also works with the [parallel](../yaml/index.md#parallel) keyword,
+The `needs` keyword also works with the [parallel](../yaml/index.md#parallel) keyword,
giving you powerful options for parallelization within your pipeline.
## Limitations
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index 324a2fa3ef9..d31bba73900 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -136,7 +136,7 @@ and [view your pipeline status](https://marketplace.visualstudio.com/items?itemN
Pipelines can be manually executed, with predefined or manually-specified [variables](../variables/index.md).
-You might do this if the results of a pipeline (for example, a code build) are required outside the normal
+You might do this if the results of a pipeline (for example, a code build) are required outside the standard
operation of the pipeline.
To execute a pipeline manually:
@@ -425,7 +425,7 @@ You can group the jobs by:
you visualize the entire pipeline, including all cross-project inter-dependencies.
If a stage contains more than 100 jobs, only the first 100 jobs are listed in the
-pipeline graph. The remaining jobs still run as normal. To see the jobs:
+pipeline graph. The remaining jobs still run as usual. To see the jobs:
- Select the pipeline, and the jobs are listed on the right side of the pipeline details page.
- On the left sidebar, select **CI/CD > Jobs**.
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index 3c73030aceb..e7922b54e02 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -20,9 +20,9 @@ If you are working on a feature or enhancement, use the
If you are not a GitLab team member, or do not have the Developer role for the GitLab repository, to update GitLab documentation:
1. Select an [issue](https://about.gitlab.com/handbook/product/ux/technical-writing/#community-contribution-opportunities) you'd like to work on.
- - You don't need an issue to open a merge request.
- For a Hackathon, mention `@docs-hackathon` in a comment and ask for the issue to be assigned to you.
To be fair to other contributors, if you see someone has already asked to work on the issue, choose another issue.
+ - If you're not taking part in a Hackathon, you don't need an issue to open a merge request.
If you are looking for issues to work on and don't see any that suit you, you can always fix [Vale](testing.md#vale) issues.
1. Go to the [GitLab repository](https://gitlab.com/gitlab-org/gitlab).
1. In the top right, select **Fork**. Forking makes a copy of the repository on GitLab.com.
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index a0441b79490..2125aea892b 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -352,7 +352,7 @@ when trying to clone via HTTPS.
When using Kerberos ticket-based authentication in an Active Directory domain,
it may be necessary to increase the maximum header size allowed by NGINX,
as extensions to the Kerberos protocol may result in HTTP authentication headers
-larger than the default size of 8kB. Configure `large_client_header_buffers`
+larger than the default size of 8 kB. Configure `large_client_header_buffers`
to a larger value in [the NGINX configuration](https://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers).
## Troubleshooting
diff --git a/doc/topics/git/getting_started.md b/doc/topics/git/getting_started.md
index a843d44aa8c..790fd3aa6c0 100644
--- a/doc/topics/git/getting_started.md
+++ b/doc/topics/git/getting_started.md
@@ -1,92 +1,11 @@
---
-stage: Create
-group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-comments: false
+redirect_to: '../../tutorials/make_your_first_git_commit.md'
+remove_date: '2023-04-23'
---
-# Getting started **(FREE)**
+This document was moved to [another location](../../tutorials/make_your_first_git_commit.md).
-## Instantiating Repositories
-
-- Create a new repository by instantiating it through:
-
- ```shell
- git init
- ```
-
-- Copy an existing project by cloning the repository through:
-
- ```shell
- git clone <url>
- ```
-
-NOTE:
-You can also clone GitLab projects with the
-[GitLab Workflow VS Code extension](../../user/project/repository/vscode.md).
-To learn more, read about the extension's
-[`Git: Clone` command](https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow#clone-gitlab-projects).
-
-## Central Repositories
-
-- To instantiate a central repository a `--bare` flag is required.
-- Bare repositories don't allow file editing or committing changes.
-- Create a bare repository with:
-
- ```shell
- git init --bare project-name.git
- ```
-
-## Instantiate workflow with clone
-
-1. Create a project in your user namespace.
- - Choose to import from **Any Repository by URL** and use <https://gitlab.com/gitlab-org/training-examples.git>.
-1. Create a '`Workspace`' directory in your home directory.
-1. Clone the '`training-examples`' project.
-
-```shell
-mkdir ~/workspace
-cd ~/workspace
-
-git clone git@gitlab.example.com:<username>/training-examples.git
-cd training-examples
-```
-
-## Git concepts
-
-**Untracked files**
-
-New files that Git has not been told to track previously.
-
-**Working area**
-
-Files that have been modified but are not committed.
-
-**Staging area**
-
-Modified files that have been marked to go in the next commit.
-
-## Committing Workflow
-
-1. Edit '`edit_this_file.rb`' in '`training-examples`'
-1. See it listed as a changed file (working area)
-1. View the differences
-1. Stage the file
-1. Commit
-1. Push the commit to the remote
-1. View the Git log
-
-```shell
-# Edit `edit_this_file.rb`
-git status
-git diff
-git add <file>
-git commit -m 'My change'
-git push origin master
-git log
-```
-
-## Note
-
-- `git fetch` vs `git pull`
-- Pull is `git fetch` + `git merge`
+<!-- This redirect file can be deleted after <2023-04-23>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/admin_area/appearance.md b/doc/user/admin_area/appearance.md
index fc42c7770f2..a65c0c86649 100644
--- a/doc/user/admin_area/appearance.md
+++ b/doc/user/admin_area/appearance.md
@@ -38,6 +38,8 @@ of the page to activate it in the GitLab instance.
## System header and footer messages
+> **Enable header and footer in emails** checkbox [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344819) in GitLab 15.9.
+
You can add a small header message, a small footer message, or both, to the interface
of your GitLab instance. These messages appear on all projects and pages of the
instance, including the sign in / sign up page. The default color is white text on
diff --git a/doc/user/enterprise_user/index.md b/doc/user/enterprise_user/index.md
deleted file mode 100644
index d8305f0288b..00000000000
--- a/doc/user/enterprise_user/index.md
+++ /dev/null
@@ -1,71 +0,0 @@
----
-stage: Manage
-group: Authentication and Authorization
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
-type: reference
----
-
-# Enterprise users **(PREMIUM SAAS)**
-
-Enterprise users have user accounts that are administered by an organization that
-has purchased a [GitLab subscription](../../subscriptions/index.md).
-
-Enterprise users are identified by the [**Enterprise** badge](../project/badges.md)
-next to their names on the [Members list](../group/manage.md#filter-and-sort-members-in-a-group).
-
-## Provision an enterprise user
-
-A user account is considered an enterprise account when:
-
-- A user without an existing GitLab user account uses the group's
- [SAML SSO](../group/saml_sso/index.md) to sign in for the first time.
-- [SCIM](../group/saml_sso/scim_setup.md) creates the user account on behalf of
- the group.
-
-A user can also [manually connect an identity provider (IdP) to a GitLab account whose email address matches the subscribing organization's domain](../group/saml_sso/index.md#linking-saml-to-your-existing-gitlabcom-account).
-By selecting **Authorize** when connecting these two accounts, the user account
-with the matching email address is classified as an enterprise user. However, this
-user account does not have an **Enterprise** badge in GitLab.
-
-Although a user can be a member of more than one group, each user account can be
-provisioned by only one group. As a result, a user is considered an enterprise
-user under one top-level group only.
-
-## Manage enterprise users in a namespace
-
-A top-level Owner of a namespace on a paid plan can retrieve information about and
-manage enterprise user accounts in that namespace.
-
-These enterprise user-specific actions are in addition to the standard
-[group member permissions](../permissions.md#group-members-permissions).
-
-### Disable two-factor authentication
-
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/9484) in GitLab 15.8.
-
-Top-level group Owners can disable two-factor authentication (2FA) for enterprise users.
-
-To disable 2FA:
-
-1. On the top bar, select **Main menu > Groups** and find your group.
-1. On the left sidebar, select **Group information > Members**.
-1. Find a user with the **Enterprise** and **2FA** badges.
-1. Select **More actions** (**{ellipsis_v}**) and select **Disable two-factor authentication**.
-
-### Prevent users from creating groups and projects outside the corporate group
-
-A SAML IdP administrator or a top-level group Owner can use a SAML response to set:
-
-- Whether users can create groups.
-- The maximum number of personal projects users can create.
-
-For more information, see the [supported user attributes for SAML responses](../group/saml_sso/index.md#supported-user-attributes).
-
-### Bypass email confirmation for provisioned users
-
-A top-level group Owner can [set up verified domains to bypass confirmation emails](../group/saml_sso/index.md#bypass-user-email-confirmation-with-verified-domains).
-
-### Get users' email addresses through the API
-
-A top-level group Owner can use the [group and project members API](../../api/members.md)
-to access users' information, including email addresses.
diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md
index aec2932a9e4..a755447c47c 100644
--- a/doc/user/group/manage.md
+++ b/doc/user/group/manage.md
@@ -131,7 +131,7 @@ Filter a group to find members. By default, all members in the group and subgrou
In lists of group members, entries can display the following badges:
- **SAML**, to indicate the member has a [SAML account](saml_sso/index.md) connected to them.
-- **Enterprise**, to indicate that the member is an [enterprise user](../enterprise_user/index.md).
+- **Enterprise**, to indicate that [SCIM created the account](saml_sso/scim_setup.md).
1. On the top bar, select **Main menu > Groups** and find your group.
1. Above the list of members, in the **Filter members** box, enter filter criteria.
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 35695c14f1a..fcb02e76095 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -333,7 +333,7 @@ To migrate users to a new email domain, users must:
## User access and management
> - SAML user provisioning [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/268142) in GitLab 13.7.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325712) in GitLab 14.0, GitLab users created by [SAML SSO](index.md#user-access-and-management) or SCIM provisioning are displayed with an ][**Enterprise**](../../enterprise_user/index.md) badge in the **Members** view.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325712) in GitLab 14.0, GitLab users created by [SAML SSO](index.md#user-access-and-management) or SCIM provisioning are displayed with an **Enterprise** badge in the **Members** view.
After group SSO is configured and enabled, users can access the GitLab.com group through the identity provider's dashboard.
If [SCIM](scim_setup.md) is configured, see [user access](scim_setup.md#user-access) on the SCIM page.
@@ -431,7 +431,7 @@ convert the information to XML. An example SAML response is shown here.
By default, users provisioned with SAML or SCIM are sent a verification email to verify their identity. Instead, you can
[configure GitLab with a custom domain](../../project/pages/custom_domains_ssl_tls_certification/index.md) and GitLab
-automatically confirms user accounts. Users still receive an [enterprise user](../../enterprise_user/index.md) welcome email. Confirmation is bypassed for
+automatically confirms user accounts. Users still receive an enterprise user welcome email. Confirmation is bypassed for
users:
- That are provisioned with SAML or SCIM.
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 79fc1ab310a..8c30c246566 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -170,7 +170,7 @@ encounter issues.
## User access
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325712) in GitLab 14.0, GitLab users created by [SAML SSO](index.md#user-access-and-management) or SCIM provisioning are displayed with an [**Enterprise**](../../enterprise_user/index.md) badge in the **Members** view.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325712) in GitLab 14.0, GitLab users created by [SAML SSO](index.md#user-access-and-management) or SCIM provisioning are displayed with an **Enterprise** badge in the **Members** view.
During the synchronization process, all new users:
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index dc650bd9482..8a855c02b8d 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -1,6 +1,6 @@
---
-stage: Create
-group: Source Code
+stage: Manage
+group: Organization
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/user/project/code_owners.md b/doc/user/project/code_owners.md
index 641dff2766e..7675260b3d4 100644
--- a/doc/user/project/code_owners.md
+++ b/doc/user/project/code_owners.md
@@ -34,18 +34,25 @@ For example:
| Code Owner approval rule | Frontend: Code Style | `*.css` files | A frontend engineer reviews CSS file changes for adherence to project style standards. |
| Code Owner approval rule | Backend: Code Review | `*.rb` files | A backend engineer reviews the logic and code style of Ruby files. |
-## Set up Code Owners
+## Code Owners file
-Create a `CODEOWNERS` file to specify users or [shared groups](members/share_project_with_groups.md)
+A `CODEOWNERS` file (with no extension) can specify users or [shared groups](members/share_project_with_groups.md)
that are responsible for specific files and directories in a repository. Each repository
-can have a single `CODEOWNERS` file. To create it:
+can have a single `CODEOWNERS` file, and it must be found one of these three locations:
+
+- In the root directory of the repository.
+- In the `.gitlab/` directory.
+- In the `docs/` directory.
+
+A CODEOWNERS file in any other location is ignored.
+
+## Set up Code Owners
-1. Choose the location where you want to specify Code Owners:
- - In the root directory of the repository
- - In the `.gitlab/` directory
- - In the `docs/` directory
+1. Create a file named `CODEOWNERS` (with no extension) in one of these locations:
-1. In that location, create a file named `CODEOWNERS`.
+- In the root directory of the repository
+- In the `.gitlab/` directory
+- In the `docs/` directory
1. In the file, enter text that follows one of these patterns:
diff --git a/doc/user/project/merge_requests/approvals/index.md b/doc/user/project/merge_requests/approvals/index.md
index 92ff78082e3..21e2055cb61 100644
--- a/doc/user/project/merge_requests/approvals/index.md
+++ b/doc/user/project/merge_requests/approvals/index.md
@@ -8,8 +8,6 @@ disqus_identifier: 'https://docs.gitlab.com/ee/user/project/merge_requests/appro
# Merge request approvals **(FREE)**
-> Redesign [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1979) in GitLab 11.8 and [feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/10685) in 12.0.
-
You can configure your merge requests so that they must be approved before
they can be merged. While [GitLab Free](https://about.gitlab.com/pricing/) allows
all users with Developer or greater [permissions](../../../permissions.md) to
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
index e654d8bc04d..9bc960f3696 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/index.md
@@ -59,8 +59,8 @@ To add your custom domain to GitLab Pages:
If this path is not visible, select **Deployments > Pages**.
[This location is part of an experiment](../index.md#menu-position-test).
-1. In the top right, select **New Domain**.
-1. In **Domain**, enter your domain.
+1. In the top-right corner, select **New Domain**.
+1. In **Domain**, enter the domain name.
1. Optional. In **Certificate**, turn off the **Automatic certificate management using Let's Encrypt** toggle to add an [SSL/TLS certificate](#adding-an-ssltls-certificate-to-pages). You can also add the certificate and key later.
1. Select **Create New Domain**.
@@ -168,10 +168,13 @@ If you're using Cloudflare, check
Once you have added all the DNS records:
-1. Go back at your project's **Settings > Pages** (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test)).
-1. Locate your domain name and select **Details**.
-1. Select the **Retry verification** button to activate your new domain.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
+1. Next to the domain name, select **Edit**.
+1. In **Verification status**, select **Retry verification** (**{retry}**).
![Verify your domain](img/retry_domain_verification_v12_0.png)
@@ -290,12 +293,28 @@ meet these requirements.
#### Steps
-- To add the certificate at the time you add a new domain, go to your
- project's **Settings > Pages > New Domain** (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test)), add the domain name and the
- certificate.
-- To add the certificate to a domain previously added, go to your
- project's **Settings > Pages**, locate your domain name, select **Details** and **Edit** to add the certificate.
+- To add the certificate at the time you add a new domain:
+
+ 1. On the top bar, select **Main menu > Projects** and find your project.
+ 1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
+ 1. In the top-right corner, select **New Domain**.
+ 1. In **Domain**, enter the domain name.
+ 1. In **Certificate**, turn off the **Automatic certificate management using Let's Encrypt** toggle to add an [SSL/TLS certificate](#adding-an-ssltls-certificate-to-pages).
+ 1. Select **Create New Domain**.
+
+- To add the certificate to a domain previously added:
+
+ 1. On the top bar, select **Main menu > Projects** and find your project.
+ 1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
+ 1. Next to the domain name, select **Edit**.
+ 1. In **Certificate**, turn off the **Automatic certificate management using Let's Encrypt** toggle to add an [SSL/TLS certificate](#adding-an-ssltls-certificate-to-pages).
+ 1. Select **Save changes**.
NOTE:
The Pages menu entry may also be located at **Deployments > Pages**, [more information](../index.md#menu-position-test)
@@ -322,9 +341,13 @@ domain (as long as you've set a valid certificate for it).
To enable this setting:
-1. Navigate to your project's **Settings > Pages** (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test)).
-1. Tick the checkbox **Force HTTPS (requires valid certificates)**.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
+1. Select the **Force HTTPS (requires valid certificates)** checkbox.
+1. Select **Save changes**.
If you use Cloudflare CDN in front of GitLab Pages, make sure to set the SSL connection setting to
`full` instead of `flexible`. For more details, see the [Cloudflare CDN directions](https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes#h_4e0d1a7c-eb71-4204-9e22-9d3ef9ef7fef).
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
index 770fb4f450a..eec624f4624 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
@@ -42,11 +42,13 @@ For **self-managed** GitLab instances, make sure your administrator has
Once you've met the requirements, enable Let's Encrypt integration:
-1. Navigate to your project's **Settings > Pages** (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test)).
-1. Find your domain and select **Details**.
-1. Select **Edit** in the top-right corner.
-1. Enable Let's Encrypt integration by switching **Automatic certificate management using Let's Encrypt**:
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
+1. Next to the domain name, select **Edit**.
+1. Turn on the **Automatic certificate management using Let's Encrypt** toggle.
![Enable Let's Encrypt](img/lets_encrypt_integration_v12_1.png)
@@ -70,31 +72,37 @@ associated Pages domain. GitLab also renews it automatically.
If you get an error **Something went wrong while obtaining the Let's Encrypt certificate**, first, make sure that your pages site is set to "Everyone" in your project's **Settings > General > Visibility**. This allows the Let's Encrypt Servers reach your pages site. Once this is confirmed, you can try obtaining the certificate again by following these steps:
-1. Go to your project's **Settings > Pages** (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test)).
-1. Select **Edit** on your domain.
-1. Select **Retry**.
-1. If you're still seeing the same error:
- 1. Make sure you have properly set only one `CNAME` or `A` DNS record for your domain.
- 1. Make sure your domain **doesn't have** an `AAAA` DNS record.
- 1. If you have a `CAA` DNS record for your domain or any higher level domains, make sure [it includes `letsencrypt.org`](https://letsencrypt.org/docs/caa/).
- 1. Make sure [your domain is verified](index.md#1-add-a-custom-domain).
- 1. Go to step 1.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
+1. Next to the domain name, select **Edit**.
+1. In **Verification status**, select **Retry verification** (**{retry}**).
+1. If you're still getting the same error:
+ 1. Make sure you have properly set only one `CNAME` or `A` DNS record for your domain.
+ 1. Make sure your domain **doesn't have** an `AAAA` DNS record.
+ 1. If you have a `CAA` DNS record for your domain or any higher level domains, make sure [it includes `letsencrypt.org`](https://letsencrypt.org/docs/caa/).
+ 1. Make sure [your domain is verified](index.md#1-add-a-custom-domain).
+ 1. Go to step 1.
### Message "GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later." hangs for more than an hour
If you've enabled Let's Encrypt integration, but a certificate is absent after an hour and you see the message, "GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later.", try to remove and add the domain for GitLab Pages again by following these steps:
-1. Go to your project's **Settings > Pages** (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test)).
-1. Select **Remove** on your domain.
-1. [Add the domain again and verify it](index.md#1-add-a-custom-domain).
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
+1. Next to the domain name, select **Remove**.
+1. [Add the domain again, and verify it](index.md#1-add-a-custom-domain).
1. [Enable Let's Encrypt integration for your domain](#enabling-lets-encrypt-integration-for-your-custom-domain).
-1. If you still see the same message after some time:
- 1. Make sure you have properly set only one `CNAME` or `A` DNS record for your domain.
- 1. Make sure your domain **doesn't have** an `AAAA` DNS record.
- 1. If you have a `CAA` DNS record for your domain or any higher level domains, make sure [it includes `letsencrypt.org`](https://letsencrypt.org/docs/caa/).
- 1. Go to step 1.
+1. If you're still getting the same error:
+ 1. Make sure you have properly set only one `CNAME` or `A` DNS record for your domain.
+ 1. Make sure your domain **doesn't have** an `AAAA` DNS record.
+ 1. If you have a `CAA` DNS record for your domain or any higher level domains, make sure [it includes `letsencrypt.org`](https://letsencrypt.org/docs/caa/).
+ 1. Go to step 1.
<!-- Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
diff --git a/doc/user/project/pages/getting_started/pages_ci_cd_template.md b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
index e0448621c6a..f596e418b02 100644
--- a/doc/user/project/pages/getting_started/pages_ci_cd_template.md
+++ b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
@@ -27,9 +27,11 @@ these steps, you may have to do additional configuration for the Pages site to g
If everything is configured correctly, the site can take approximately 30 minutes to deploy.
To view the pipeline, go to **CI/CD > Pipelines**.
+
When the pipeline is finished, go to **Settings > Pages** to find the link to
-your Pages website. (Note: this may also be
-located at **Deployments > Pages**, [more information](../index.md#menu-position-test))
+your Pages website.
+If this path is not visible, select **Deployments > Pages**.
+[This location is part of an experiment](../index.md#menu-position-test).
For every change pushed to your repository, GitLab CI/CD runs a new pipeline
that immediately publishes your changes to the Pages site.
diff --git a/doc/user/project/pages/getting_started/pages_forked_sample_project.md b/doc/user/project/pages/getting_started/pages_forked_sample_project.md
index e1a245851cb..b991a0ebd59 100644
--- a/doc/user/project/pages/getting_started/pages_forked_sample_project.md
+++ b/doc/user/project/pages/getting_started/pages_forked_sample_project.md
@@ -24,9 +24,10 @@ To fork a sample project and create a Pages website:
GitLab CI/CD builds and deploys your site.
The site can take approximately 30 minutes to deploy.
-When the pipeline is finished, go to **Settings > Pages** to find the link
-to your website from your project. (Note: this may also be
-located at **Deployments > Pages**, [more information](../index.md#menu-position-test))
+When the pipeline is finished, go to **Settings > Pages** to find the link to
+your Pages website.
+If this path is not visible, select **Deployments > Pages**.
+[This location is part of an experiment](../index.md#menu-position-test).
For every change pushed to your repository, GitLab CI/CD runs a new pipeline
that immediately publishes your changes to the Pages site.
diff --git a/doc/user/project/pages/getting_started/pages_from_scratch.md b/doc/user/project/pages/getting_started/pages_from_scratch.md
index 82e544b0701..a3d6c8f75f9 100644
--- a/doc/user/project/pages/getting_started/pages_from_scratch.md
+++ b/doc/user/project/pages/getting_started/pages_from_scratch.md
@@ -175,10 +175,11 @@ deploy your website:
1. Save and commit the `.gitlab-ci.yml` file.
1. Go to **CI/CD > Pipelines** to watch the pipeline.
-1. When the pipeline succeeds, go to **Settings > Pages**
- to view the URL where your site is now available. (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test))
+1. When the pipeline is finished, go to **Settings > Pages** to find the link to
+ your Pages website.
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
When this `pages` job completes successfully, a special `pages:deploy` job
appears in the pipeline view. It prepares the content of the website for the
GitLab Pages daemon. GitLab runs it in the background and doesn't use a runner.
diff --git a/doc/user/project/pages/getting_started/pages_new_project_template.md b/doc/user/project/pages/getting_started/pages_new_project_template.md
index a9988b1fb99..a301d2b64be 100644
--- a/doc/user/project/pages/getting_started/pages_new_project_template.md
+++ b/doc/user/project/pages/getting_started/pages_new_project_template.md
@@ -24,8 +24,9 @@ configured to generate a Pages site.
site.
When the pipeline is finished, go to **Settings > Pages** to find the link to
-your Pages website. (Note: this may also be
-located at **Deployments > Pages**, [more information](../index.md#menu-position-test))
+your Pages website.
+If this path is not visible, select **Deployments > Pages**.
+[This location is part of an experiment](../index.md#menu-position-test).
For every change pushed to your repository, GitLab CI/CD runs a new pipeline
that immediately publishes your changes to the Pages site.
diff --git a/doc/user/project/pages/getting_started/pages_ui.md b/doc/user/project/pages/getting_started/pages_ui.md
index 984c8e5c619..91c2c532a9a 100644
--- a/doc/user/project/pages/getting_started/pages_ui.md
+++ b/doc/user/project/pages/getting_started/pages_ui.md
@@ -34,8 +34,10 @@ a pipeline deploys your Pages website.
To complete the setup and generate a GitLab Pages deployment:
1. On the top bar, select **Main menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > Pages** (Note: this may also be
- located at **Deployments > Pages**, [more information](../index.md#menu-position-test)).
+1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](../index.md#menu-position-test).
A **Get Started with Pages** form appears. If this form is not available,
see [Troubleshooting](#if-the-get-started-with-pages-form-is-not-available).
1. For **Step 1**, enter an image name and verify that your files are in a `public` folder.
@@ -61,10 +63,10 @@ and on the right side, select **Download artifacts**.
### If the `Get Started with Pages` form is not available
-When you go to **Settings > Pages**, the form is not available if you:
+The `Get Started with Pages` form is not available if you:
- Deployed a GitLab Pages site before.
-- Committed a `.gitlab-ci.yml` through the forms at least one time.
+- Committed `.gitlab-ci.yml` through the forms at least one time.
To fix this issue:
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index a9b8960d629..260a866f9a5 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -68,6 +68,9 @@ To remove your pages:
1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Pages**.
+
+ If this path is not visible, select **Deployments > Pages**.
+ [This location is part of an experiment](index.md#menu-position-test).
1. Select **Remove pages**.
## Subdomains of subdomains
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index 6b67ffd0e59..fdf6a7c72ab 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -180,6 +180,47 @@ you can sign individual commits manually, or configure Git to default to signed
git config --global commit.gpgsign true
```
+#### Set signing key conditionally
+
+If you maintain signing keys for separate purposes, such as work and personal
+use, use an `IncludeIf` statement in your `.gitconfig` file to set which key
+you sign commits with.
+
+Prerequisites:
+
+- Requires Git version 2.13 or later.
+
+1. In the same directory as your main `~/.gitconfig` file, create a second file,
+ such as `.gitconfig-gitlab`.
+1. In your main `~/.gitconfig` file, add your Git settings for work in non-GitLab projects.
+1. Append this information to the end of your main `~/.gitconfig` file:
+
+ ```ini
+ # The contents of this file are included only for GitLab.com URLs
+ [includeIf "hasconfig:remote.*.url:https://gitlab.com/**"]
+
+ # Edit this line to point to your alternate configuration file
+ path = ~/.gitconfig-gitlab
+ ```
+
+1. In your alternate `.gitconfig-gitlab` file, add the configuration overrides to
+ use when you're committing to a GitLab repository. All settings from your
+ main `~/.gitconfig` file are retained unless you explicitly override them.
+ In this example,
+
+ ```ini
+ # Alternate ~/.gitconfig-gitlab file
+ # These values are used for repositories matching the string 'gitlab.com',
+ # and override their corresponding values in ~/.gitconfig
+
+ [user]
+ email = you@example.com
+ signingkey = <KEY ID>
+
+ [commit]
+ gpgsign = true
+ ```
+
## Verify commits
You can review commits for a merge request, or for an entire project:
diff --git a/doc/user/project/repository/web_editor.md b/doc/user/project/repository/web_editor.md
index b5b2b8aaae9..80b0ada6f7b 100644
--- a/doc/user/project/repository/web_editor.md
+++ b/doc/user/project/repository/web_editor.md
@@ -6,10 +6,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Web Editor **(FREE)**
-You can use the Web Editor to make changes directly from the GitLab UI instead of
-cloning a project and using the command line.
+You can use the Web Editor to make changes to a single file directly from the
+GitLab UI. To make changes to multiple files, see [Web IDE](../web_ide/index.md).
-From the project dashboard or repository, you can:
+In the Web Editor, you can:
- [Create a file](#create-a-file).
- [Edit a file](#edit-a-file).
@@ -18,8 +18,8 @@ From the project dashboard or repository, you can:
- [Create a branch](#create-a-branch).
- [Create a tag](#create-a-tag).
-Your [primary email address](../../../user/profile/index.md#change-the-email-displayed-on-your-commits)
-is used by default for any change you commit through the Web Editor.
+Your [primary email address is used by default](../../../user/profile/index.md#change-the-email-displayed-on-your-commits)
+for any change you commit through the Web Editor.
## Create a file
@@ -28,18 +28,22 @@ To create a text file in the Web Editor:
1. On the top bar, select **Main menu > Projects** and find your project.
1. From the project dashboard or repository, next to the branch name, select the plus icon (**{plus}**).
1. From the dropdown list, select **New file**.
-1. Complete the fields.
- - From the **Select a template type** dropdown list, you can apply a template to the new file.
- - To create a merge request with the new file, ensure the **Start a new merge request with these changes** checkbox is selected.
+1. Complete the fields. To create a merge request with the new file, ensure the **Start a new merge request with these changes** checkbox is selected.
1. Select **Commit changes**.
## Edit a file
-To edit a file in the Web Editor:
+To edit a text file in the Web Editor:
1. On the top bar, select **Main menu > Projects** and find your project.
1. Go to your file.
-1. Next to the display buttons, select **Edit**.
+1. In the upper-right corner of the file, select **Edit**.
+
+ If **Edit** is not visible:
+
+ 1. Next to **Open in Web IDE** or **Open in Gitpod**, select the down arrow (**{chevron-lg-down}**).
+ 1. From the dropdown list, select **Edit** as your default setting.
+ 1. Select **Edit**.
### Keyboard shortcuts
diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md
index 83cf47d508c..cc195c3c959 100644
--- a/doc/user/project/service_desk.md
+++ b/doc/user/project/service_desk.md
@@ -113,6 +113,8 @@ With Service Desk, you can use templates for:
#### Email header and footer **(FREE SELF)**
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344819) in GitLab 15.9.
+
Instance administrators can add a small header or footer to the GitLab instance and make them
visible in the email template. For more information, see
[System header and footer messages](../admin_area/appearance.md#system-header-and-footer-messages).
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 4648df7dbd7..ead1f43e4a3 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -6,9 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Web IDE **(FREE)**
-The Web Integrated Development Environment (IDE) editor streamlines the process
-to contribute changes to your projects, by providing an advanced editor with
-commit staging.
+The Web IDE is an advanced editor with commit staging.
+You can use the Web IDE to make changes to multiple files directly from the
+GitLab UI. For a more basic implementation, see [Web Editor](../repository/web_editor.md).
NOTE:
The Web IDE is being updated to use VS Code. For details,
diff --git a/lib/api/entities/ci/job_basic.rb b/lib/api/entities/ci/job_basic.rb
index 3cbb8aad313..526dfba05f2 100644
--- a/lib/api/entities/ci/job_basic.rb
+++ b/lib/api/entities/ci/job_basic.rb
@@ -15,6 +15,7 @@ module API
expose :created_at, documentation: { type: 'dateTime', example: '2015-12-24T15:51:21.880Z' }
expose :started_at, documentation: { type: 'dateTime', example: '2015-12-24T17:54:30.733Z' }
expose :finished_at, documentation: { type: 'dateTime', example: '2015-12-24T17:54:31.198Z' }
+ expose :erased_at, documentation: { type: 'dateTime', example: '2015-12-24T18:00:29.728Z' }
expose :duration,
documentation: { type: 'number', format: 'float', desc: 'Time spent running', example: 0.465 }
expose :queued_duration,
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 991a38f1ddd..bd77f3b3267 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -21378,6 +21378,9 @@ msgstr ""
msgid "Improve quality with test cases"
msgstr ""
+msgid "In GitLab"
+msgstr ""
+
msgid "In case of pull mirroring, your user will be the author of all events in the activity feed that are the result of an update, like new branches being created or new commits being pushed to existing branches."
msgstr ""
@@ -21390,9 +21393,15 @@ msgstr ""
msgid "In the background, we're attempting to connect you again."
msgstr ""
+msgid "In this group"
+msgstr ""
+
msgid "In this page you will find information about the settings that are used in your current instance."
msgstr ""
+msgid "In this project"
+msgstr ""
+
msgid "In use"
msgstr ""
@@ -22143,6 +22152,9 @@ msgstr ""
msgid "Incident|Add new timeline event"
msgstr ""
+msgid "Incident|Adding an event tag associates the timeline comment with specific incident metrics."
+msgstr ""
+
msgid "Incident|Alert details"
msgstr ""
@@ -22176,6 +22188,9 @@ msgstr ""
msgid "Incident|Error updating incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Event tag"
+msgstr ""
+
msgid "Incident|Incident"
msgstr ""
@@ -43370,9 +43385,6 @@ msgstr ""
msgid "This process deletes the project repository and all related resources."
msgstr ""
-msgid "This project"
-msgstr ""
-
msgid "This project can be restored until %{date}."
msgstr ""
diff --git a/package.json b/package.json
index 0adfcba41b6..cf415808e85 100644
--- a/package.json
+++ b/package.json
@@ -57,8 +57,8 @@
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.1.0",
- "@gitlab/svgs": "3.16.0",
- "@gitlab/ui": "52.11.0",
+ "@gitlab/svgs": "3.17.0",
+ "@gitlab/ui": "52.13.1",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20230120231236",
"@rails/actioncable": "6.1.4-7",
diff --git a/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb b/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb
index c6836d11803..805e5ce782a 100644
--- a/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb
+++ b/qa/qa/specs/features/shared_contexts/advanced_search_shared_context.rb
@@ -8,18 +8,25 @@ module QA
QA::EE::Resource::Settings::Elasticsearch.fabricate_via_api! unless advanced_search_on
end
- after do
- Runtime::Search.disable_elasticsearch(api_client) if !advanced_search_on && !api_client.nil?
- end
+ # TODO: convert check_advanced_search_status method to use the API instead of the UI once the functionality exists
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/382849 and then we can resume turning off advanced search after the
+ # tests as in the `after` block here. For now the advanced search tests will have the side effect of turning on
+ # advanced search if it wasn't enabled before the tests run.
+
+ # after do
+ # Runtime::Search.disable_elasticsearch(api_client) if !advanced_search_on && !api_client.nil?
+ # end
- # TODO: convert this method to use the API instead of the UI once the functionality exists
- # https://gitlab.com/gitlab-org/gitlab/-/issues/382849
def check_advanced_search_status
Flow::Login.sign_in
- QA::Page::Main::Menu.perform do |menu|
- menu.search_for('lorem ipsum')
+ QA::Support::Retrier.retry_on_exception(
+ max_attempts: Runtime::Search::RETRY_MAX_ITERATION,
+ sleep_interval: Runtime::Search::RETRY_SLEEP_INTERVAL) do
+ QA::Page::Main::Menu.perform do |menu|
+ menu.search_for('lorem ipsum')
+ end
+ page.has_text?('Advanced search is enabled')
end
- page.has_text?('Advanced search is enabled')
end
end
end
diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb
index 4b8636da6b4..46cc7184e89 100644
--- a/spec/features/admin/admin_sees_background_migrations_spec.rb
+++ b/spec/features/admin/admin_sees_background_migrations_spec.rb
@@ -92,11 +92,11 @@ RSpec.describe "Admin > Admin sees background migrations", feature_category: :da
expect(page).not_to have_content('Paused')
expect(page).to have_content('Active')
- click_button('Pause')
+ click_on('Pause')
expect(page).not_to have_content('Active')
expect(page).to have_content('Paused')
- click_button('Resume')
+ click_on('Resume')
expect(page).not_to have_content('Paused')
expect(page).to have_content('Active')
end
@@ -123,7 +123,7 @@ RSpec.describe "Admin > Admin sees background migrations", feature_category: :da
tab = find_link 'Failed'
tab.click
- expect(page).to have_selector("[method='post'][action='/admin/background_migrations/#{failed_migration.id}/retry?database=main']")
+ expect(page).to have_selector("[data-method='post'][href='/admin/background_migrations/#{failed_migration.id}/retry?database=main']")
end
end
@@ -144,7 +144,7 @@ RSpec.describe "Admin > Admin sees background migrations", feature_category: :da
expect(page).to have_content('0.00%')
expect(page).to have_content(failed_migration.status_name.to_s)
- click_button('Retry')
+ click_on('Retry')
expect(page).not_to have_content(failed_migration.job_class_name)
expect(page).not_to have_content(failed_migration.table_name)
expect(page).not_to have_content('0.00%')
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 2ac86ab9f49..f0482d8c511 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -861,7 +861,7 @@ RSpec.describe 'Admin updates settings', feature_category: :not_owned do
context 'Nav bar' do
it 'shows default help links in nav' do
- default_support_url = "https://#{ApplicationHelper.promo_host}/getting-help/"
+ default_support_url = "https://#{ApplicationHelper.promo_host}/get-help/"
visit root_dashboard_path
diff --git a/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb b/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
index 01c202baf70..ec00dcaf046 100644
--- a/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
+++ b/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe 'User activates Slack notifications', :js, feature_category: :int
context 'when integration is not configured yet' do
before do
+ stub_feature_flags(integration_slack_app_notifications: false)
visit_project_integration('Slack notifications')
end
diff --git a/spec/fixtures/api/schemas/public_api/v4/job.json b/spec/fixtures/api/schemas/public_api/v4/job.json
index f6b12d3a1c0..6265fbcff69 100644
--- a/spec/fixtures/api/schemas/public_api/v4/job.json
+++ b/spec/fixtures/api/schemas/public_api/v4/job.json
@@ -11,6 +11,7 @@
"created_at",
"started_at",
"finished_at",
+ "erased_at",
"duration",
"queued_duration",
"user",
@@ -35,6 +36,7 @@
"created_at": { "type": "string" },
"started_at": { "type": ["null", "string"] },
"finished_at": { "type": ["null", "string"] },
+ "erased_at": { "type": ["null", "string"] },
"duration": { "type": ["null", "number"] },
"queued_duration": { "type": ["null", "number"] },
"user": { "$ref": "user/basic.json" },
diff --git a/spec/frontend/admin/broadcast_messages/components/base_spec.js b/spec/frontend/admin/broadcast_messages/components/base_spec.js
index 020e1c1d7c1..79bde54286e 100644
--- a/spec/frontend/admin/broadcast_messages/components/base_spec.js
+++ b/spec/frontend/admin/broadcast_messages/components/base_spec.js
@@ -6,6 +6,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { redirectTo } from '~/lib/utils/url_utility';
import BroadcastMessagesBase from '~/admin/broadcast_messages/components/base.vue';
import MessagesTable from '~/admin/broadcast_messages/components/messages_table.vue';
@@ -86,7 +87,7 @@ describe('BroadcastMessagesBase', () => {
it('removes a deleted message from visibleMessages on success', async () => {
createComponent();
const { id, delete_path } = MOCK_MESSAGES[0];
- axiosMock.onDelete(delete_path).replyOnce(200);
+ axiosMock.onDelete(delete_path).replyOnce(HTTP_STATUS_OK);
findTable().vm.$emit('delete-message', id);
await waitForPromises();
@@ -102,7 +103,7 @@ describe('BroadcastMessagesBase', () => {
const { id, delete_path } = messages[0];
createComponent({ messages, messagesCount: messages.length });
- axiosMock.onDelete(delete_path).replyOnce(200);
+ axiosMock.onDelete(delete_path).replyOnce(HTTP_STATUS_OK);
findTable().vm.$emit('delete-message', id);
await waitForPromises();
diff --git a/spec/frontend/feature_flags/store/edit/actions_spec.js b/spec/frontend/feature_flags/store/edit/actions_spec.js
index 7132e83a940..bcacfa9fda9 100644
--- a/spec/frontend/feature_flags/store/edit/actions_spec.js
+++ b/spec/frontend/feature_flags/store/edit/actions_spec.js
@@ -17,6 +17,7 @@ import * as types from '~/feature_flags/store/edit/mutation_types';
import state from '~/feature_flags/store/edit/state';
import { mapStrategiesToRails } from '~/feature_flags/store/helpers';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
jest.mock('~/lib/utils/url_utility');
@@ -55,7 +56,9 @@ describe('Feature flags Edit Module actions', () => {
},
],
};
- mock.onPut(mockedState.endpoint, mapStrategiesToRails(featureFlag)).replyOnce(200);
+ mock
+ .onPut(mockedState.endpoint, mapStrategiesToRails(featureFlag))
+ .replyOnce(HTTP_STATUS_OK);
return testAction(
updateFeatureFlag,
@@ -155,7 +158,7 @@ describe('Feature flags Edit Module actions', () => {
describe('success', () => {
it('dispatches requestFeatureFlag and receiveFeatureFlagSuccess', () => {
- mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 1 });
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(HTTP_STATUS_OK, { id: 1 });
return testAction(
fetchFeatureFlag,
diff --git a/spec/frontend/feature_flags/store/new/actions_spec.js b/spec/frontend/feature_flags/store/new/actions_spec.js
index dbe6669c868..c8bf05e4dbd 100644
--- a/spec/frontend/feature_flags/store/new/actions_spec.js
+++ b/spec/frontend/feature_flags/store/new/actions_spec.js
@@ -11,6 +11,7 @@ import {
import * as types from '~/feature_flags/store/new/mutation_types';
import state from '~/feature_flags/store/new/state';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
jest.mock('~/lib/utils/url_utility');
@@ -48,7 +49,9 @@ describe('Feature flags New Module Actions', () => {
},
],
};
- mock.onPost(mockedState.endpoint, mapStrategiesToRails(actionParams)).replyOnce(200);
+ mock
+ .onPost(mockedState.endpoint, mapStrategiesToRails(actionParams))
+ .replyOnce(HTTP_STATUS_OK);
return testAction(
createFeatureFlag,
diff --git a/spec/frontend/gpg_badges_spec.js b/spec/frontend/gpg_badges_spec.js
index 0a1596b492d..4c2242bae4b 100644
--- a/spec/frontend/gpg_badges_spec.js
+++ b/spec/frontend/gpg_badges_spec.js
@@ -3,6 +3,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'spec/test_constants';
import GpgBadges from '~/gpg_badges';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
describe('GpgBadges', () => {
let mock;
@@ -63,7 +64,7 @@ describe('GpgBadges', () => {
});
it('fetches commit signatures', async () => {
- mock.onGet(dummyUrl).replyOnce(200);
+ mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK);
await GpgBadges.fetch();
@@ -75,7 +76,7 @@ describe('GpgBadges', () => {
});
it('fetches commit signatures with search parameters with spaces', async () => {
- mock.onGet(dummyUrl).replyOnce(200);
+ mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK);
setForm({ search: 'my search' });
await GpgBadges.fetch();
@@ -88,7 +89,7 @@ describe('GpgBadges', () => {
});
it('fetches commit signatures with search parameters with plus symbols', async () => {
- mock.onGet(dummyUrl).replyOnce(200);
+ mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK);
setForm({ search: 'my+search' });
await GpgBadges.fetch();
@@ -101,7 +102,7 @@ describe('GpgBadges', () => {
});
it('displays a loading spinner', async () => {
- mock.onGet(dummyUrl).replyOnce(200);
+ mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK);
await GpgBadges.fetch();
expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
@@ -111,7 +112,7 @@ describe('GpgBadges', () => {
});
it('replaces the loading spinner', async () => {
- mock.onGet(dummyUrl).replyOnce(200, dummyResponse);
+ mock.onGet(dummyUrl).replyOnce(HTTP_STATUS_OK, dummyResponse);
await GpgBadges.fetch();
expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
diff --git a/spec/frontend/groups/components/invite_members_banner_spec.js b/spec/frontend/groups/components/invite_members_banner_spec.js
index d25b45bd662..4a385cb00ee 100644
--- a/spec/frontend/groups/components/invite_members_banner_spec.js
+++ b/spec/frontend/groups/components/invite_members_banner_spec.js
@@ -6,6 +6,7 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import InviteMembersBanner from '~/groups/components/invite_members_banner.vue';
import eventHub from '~/invite_members/event_hub';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
jest.mock('~/lib/utils/common_utils');
@@ -89,7 +90,7 @@ describe('InviteMembersBanner', () => {
it('sends the dismissEvent when the banner is dismissed', () => {
mockTrackingOnWrapper();
- mockAxios.onPost(provide.calloutsPath).replyOnce(200);
+ mockAxios.onPost(provide.calloutsPath).replyOnce(HTTP_STATUS_OK);
const dismissEvent = 'invite_members_banner_dismissed';
wrapper.findComponent(GlBanner).vm.$emit('close');
@@ -136,7 +137,7 @@ describe('InviteMembersBanner', () => {
});
it('should close the banner when dismiss is clicked', async () => {
- mockAxios.onPost(provide.calloutsPath).replyOnce(200);
+ mockAxios.onPost(provide.calloutsPath).replyOnce(HTTP_STATUS_OK);
expect(wrapper.findComponent(GlBanner).exists()).toBe(true);
wrapper.findComponent(GlBanner).vm.$emit('close');
diff --git a/spec/frontend/integrations/index/components/integrations_table_spec.js b/spec/frontend/integrations/index/components/integrations_table_spec.js
index bfe0a5987b4..976c7b74890 100644
--- a/spec/frontend/integrations/index/components/integrations_table_spec.js
+++ b/spec/frontend/integrations/index/components/integrations_table_spec.js
@@ -1,5 +1,6 @@
-import { GlTable, GlIcon } from '@gitlab/ui';
+import { GlTable, GlIcon, GlLink } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
+import { INTEGRATION_TYPE_SLACK } from '~/integrations/constants';
import IntegrationsTable from '~/integrations/index/components/integrations_table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -10,12 +11,17 @@ describe('IntegrationsTable', () => {
const findTable = () => wrapper.findComponent(GlTable);
- const createComponent = (propsData = {}) => {
+ const createComponent = (propsData = {}, flagIsOn = false) => {
wrapper = mount(IntegrationsTable, {
propsData: {
integrations: mockActiveIntegrations,
...propsData,
},
+ provide: {
+ glFeatures: {
+ integrationSlackAppNotifications: flagIsOn,
+ },
+ },
});
};
@@ -50,4 +56,51 @@ describe('IntegrationsTable', () => {
expect(findTable().findComponent(GlIcon).exists()).toBe(shouldRenderActiveIcon);
});
});
+
+ describe('integrations filtering', () => {
+ const slackActive = {
+ ...mockActiveIntegrations[0],
+ name: INTEGRATION_TYPE_SLACK,
+ title: 'Slack',
+ };
+ const slackInactive = {
+ ...mockInactiveIntegrations[0],
+ name: INTEGRATION_TYPE_SLACK,
+ title: 'Slack',
+ };
+
+ describe.each`
+ desc | flagIsOn | integrations | expectedIntegrations
+ ${'only active'} | ${false} | ${mockActiveIntegrations} | ${mockActiveIntegrations}
+ ${'only active'} | ${true} | ${mockActiveIntegrations} | ${mockActiveIntegrations}
+ ${'only inactive'} | ${true} | ${mockInactiveIntegrations} | ${mockInactiveIntegrations}
+ ${'only inactive'} | ${false} | ${mockInactiveIntegrations} | ${mockInactiveIntegrations}
+ ${'active and inactive'} | ${true} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]}
+ ${'active and inactive'} | ${false} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]}
+ ${'Slack active with active'} | ${false} | ${[slackActive, ...mockActiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations]}
+ ${'Slack active with active'} | ${true} | ${[slackActive, ...mockActiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations]}
+ ${'Slack active with inactive'} | ${false} | ${[slackActive, ...mockInactiveIntegrations]} | ${[slackActive, ...mockInactiveIntegrations]}
+ ${'Slack active with inactive'} | ${true} | ${[slackActive, ...mockInactiveIntegrations]} | ${[slackActive, ...mockInactiveIntegrations]}
+ ${'Slack inactive with active'} | ${false} | ${[slackInactive, ...mockActiveIntegrations]} | ${[slackInactive, ...mockActiveIntegrations]}
+ ${'Slack inactive with active'} | ${true} | ${[slackInactive, ...mockActiveIntegrations]} | ${mockActiveIntegrations}
+ ${'Slack inactive with inactive'} | ${false} | ${[slackInactive, ...mockInactiveIntegrations]} | ${[slackInactive, ...mockInactiveIntegrations]}
+ ${'Slack inactive with inactive'} | ${true} | ${[slackInactive, ...mockInactiveIntegrations]} | ${mockInactiveIntegrations}
+ ${'Slack active with active and inactive'} | ${true} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]}
+ ${'Slack active with active and inactive'} | ${false} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[slackActive, ...mockActiveIntegrations, ...mockInactiveIntegrations]}
+ ${'Slack inactive with active and inactive'} | ${true} | ${[slackInactive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[...mockActiveIntegrations, ...mockInactiveIntegrations]}
+ ${'Slack inactive with active and inactive'} | ${false} | ${[slackInactive, ...mockActiveIntegrations, ...mockInactiveIntegrations]} | ${[slackInactive, ...mockActiveIntegrations, ...mockInactiveIntegrations]}
+ `('when $desc and flag "$flagIsOn"', ({ flagIsOn, integrations, expectedIntegrations }) => {
+ beforeEach(() => {
+ createComponent({ integrations }, flagIsOn);
+ });
+
+ it('renders correctly', () => {
+ const links = wrapper.findAllComponents(GlLink);
+ expect(links).toHaveLength(expectedIntegrations.length);
+ expectedIntegrations.forEach((integration, index) => {
+ expect(links.at(index).text()).toBe(integration.title);
+ });
+ });
+ });
+ });
});
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_tags_popover_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_tags_popover_spec.js
new file mode 100644
index 00000000000..b39e96127c3
--- /dev/null
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_tags_popover_spec.js
@@ -0,0 +1,48 @@
+import { nextTick } from 'vue';
+import { GlIcon, GlPopover, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import TimelineEventsTagsPopover from '~/issues/show/components/incidents/timeline_events_tags_popover.vue';
+
+describe('TimelineEventsTagsPopover component', () => {
+ let wrapper;
+
+ const mountComponent = () => {
+ wrapper = shallowMount(TimelineEventsTagsPopover, {
+ stubs: {
+ GlPopover,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ const findQuestionIcon = () => wrapper.findComponent(GlIcon);
+ const findPopover = () => wrapper.findComponent(GlPopover);
+ const findDocumentationLink = () => findPopover().findComponent(GlLink);
+
+ describe('question icon', () => {
+ it('should open a popover with a link when hovered', async () => {
+ findQuestionIcon().vm.$emit('hover');
+ await nextTick();
+
+ expect(findPopover().exists()).toBe(true);
+ expect(findDocumentationLink().exists()).toBe(true);
+ });
+ });
+
+ describe('documentation link', () => {
+ it('redirects to a correct documentation page', async () => {
+ findQuestionIcon().vm.$emit('hover');
+ await nextTick();
+
+ expect(findDocumentationLink().attributes('href')).toBe(
+ helpPagePath('/ee/operations/incident_management/incident_timeline_events', {
+ anchor: 'incident-tags',
+ }),
+ );
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
index 1d9c5aacf43..78eef205b49 100644
--- a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
+++ b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
@@ -1,6 +1,6 @@
import { ApolloLink, Observable } from '@apollo/client/core';
import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link';
-import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
+import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
describe('StartupJSLink', () => {
const FORWARDED_RESPONSE = { data: 'FORWARDED_RESPONSE' };
@@ -38,7 +38,7 @@ describe('StartupJSLink', () => {
let startupLink;
let link;
- function mockFetchCall(status = 200, response = STARTUP_JS_RESPONSE) {
+ function mockFetchCall(status = HTTP_STATUS_OK, response = STARTUP_JS_RESPONSE) {
const p = {
ok: status >= 200 && status < 300,
status,
@@ -210,7 +210,7 @@ describe('StartupJSLink', () => {
window.gl = {
startup_graphql_calls: [
{
- fetchCall: mockFetchCall(200, ERROR_RESPONSE),
+ fetchCall: mockFetchCall(HTTP_STATUS_OK, ERROR_RESPONSE),
query: STARTUP_JS_QUERY,
variables: { id: 3 },
},
@@ -227,7 +227,7 @@ describe('StartupJSLink', () => {
window.gl = {
startup_graphql_calls: [
{
- fetchCall: mockFetchCall(200, { 'no-data': 'yay' }),
+ fetchCall: mockFetchCall(HTTP_STATUS_OK, { 'no-data': 'yay' }),
query: STARTUP_JS_QUERY,
variables: { id: 3 },
},
@@ -340,7 +340,7 @@ describe('StartupJSLink', () => {
variables: { id: 3 },
},
{
- fetchCall: mockFetchCall(200, STARTUP_JS_RESPONSE_TWO),
+ fetchCall: mockFetchCall(HTTP_STATUS_OK, STARTUP_JS_RESPONSE_TWO),
query: STARTUP_JS_QUERY_TWO,
variables: { id: 3 },
},
diff --git a/spec/frontend/lib/utils/axios_startup_calls_spec.js b/spec/frontend/lib/utils/axios_startup_calls_spec.js
index fc5bd3b811c..4471b781446 100644
--- a/spec/frontend/lib/utils/axios_startup_calls_spec.js
+++ b/spec/frontend/lib/utils/axios_startup_calls_spec.js
@@ -1,7 +1,7 @@
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import setupAxiosStartupCalls from '~/lib/utils/axios_startup_calls';
-import { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status';
+import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
describe('setupAxiosStartupCalls', () => {
const AXIOS_RESPONSE = { text: 'AXIOS_RESPONSE' };
@@ -32,9 +32,9 @@ describe('setupAxiosStartupCalls', () => {
beforeEach(() => {
window.gl = {};
mock = new MockAdapter(axios);
- mock.onGet('/non-startup').reply(200, AXIOS_RESPONSE);
- mock.onGet('/startup').reply(200, AXIOS_RESPONSE);
- mock.onGet('/startup-failing').reply(200, AXIOS_RESPONSE);
+ mock.onGet('/non-startup').reply(HTTP_STATUS_OK, AXIOS_RESPONSE);
+ mock.onGet('/startup').reply(HTTP_STATUS_OK, AXIOS_RESPONSE);
+ mock.onGet('/startup-failing').reply(HTTP_STATUS_OK, AXIOS_RESPONSE);
});
afterEach(() => {
@@ -53,7 +53,7 @@ describe('setupAxiosStartupCalls', () => {
beforeEach(() => {
window.gl.startup_calls = {
'/startup': {
- fetchCall: mockFetchCall(200),
+ fetchCall: mockFetchCall(HTTP_STATUS_OK),
},
'/startup-failing': {
fetchCall: mockFetchCall(HTTP_STATUS_BAD_REQUEST),
@@ -81,7 +81,7 @@ describe('setupAxiosStartupCalls', () => {
const { headers, data, status, statusText } = await axios.get('/startup');
expect(headers).toEqual({ 'content-type': 'application/json' });
- expect(status).toBe(200);
+ expect(status).toBe(HTTP_STATUS_OK);
expect(statusText).toBe('MOCK-FETCH 200');
expect(data).toEqual(STARTUP_JS_RESPONSE);
expect(data).not.toEqual(AXIOS_RESPONSE);
@@ -127,7 +127,7 @@ describe('setupAxiosStartupCalls', () => {
it('removes GitLab Base URL from startup call', async () => {
window.gl.startup_calls = {
'/startup': {
- fetchCall: mockFetchCall(200),
+ fetchCall: mockFetchCall(HTTP_STATUS_OK),
},
};
setupAxiosStartupCalls(axios);
@@ -140,7 +140,7 @@ describe('setupAxiosStartupCalls', () => {
it('sorts the params in the requested API url', async () => {
window.gl.startup_calls = {
'/startup?alpha=true&bravo=true': {
- fetchCall: mockFetchCall(200),
+ fetchCall: mockFetchCall(HTTP_STATUS_OK),
},
};
setupAxiosStartupCalls(axios);
diff --git a/spec/frontend/lib/utils/axios_utils_spec.js b/spec/frontend/lib/utils/axios_utils_spec.js
index 27c580a4d8b..2656fb1d648 100644
--- a/spec/frontend/lib/utils/axios_utils_spec.js
+++ b/spec/frontend/lib/utils/axios_utils_spec.js
@@ -28,8 +28,8 @@ describe('axios_utils', () => {
return axios.waitForAll().finally(() => {
expect(handler).toHaveBeenCalledTimes(2);
- expect(handler.mock.calls[0][0].status).toBe(200);
- expect(handler.mock.calls[1][0].response.status).toBe(500);
+ expect(handler.mock.calls[0][0].status).toBe(HTTP_STATUS_OK);
+ expect(handler.mock.calls[1][0].response.status).toBe(HTTP_STATUS_INTERNAL_SERVER_ERROR);
});
});
});
diff --git a/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap
index 08487a7a796..4483c9fd39f 100644
--- a/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap
+++ b/spec/frontend/monitoring/components/__snapshots__/empty_state_spec.js.snap
@@ -5,6 +5,7 @@ exports[`EmptyState shows gettingStarted state 1`] = `
<!---->
<gl-empty-state-stub
+ contentclass=""
description="Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
invertindarkmode="true"
primarybuttonlink="/clustersPath"
@@ -22,6 +23,7 @@ exports[`EmptyState shows noData state 1`] = `
<!---->
<gl-empty-state-stub
+ contentclass=""
description="You are connected to the Prometheus server, but there is currently no data to display."
invertindarkmode="true"
primarybuttonlink="/settingsPath"
@@ -39,6 +41,7 @@ exports[`EmptyState shows unableToConnect state 1`] = `
<!---->
<gl-empty-state-stub
+ contentclass=""
description="Ensure connectivity is available from the GitLab server to the Prometheus server"
invertindarkmode="true"
primarybuttonlink="/documentationPath"
diff --git a/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap
index 1d7ff420a17..42a16a39dfd 100644
--- a/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap
+++ b/spec/frontend/monitoring/components/__snapshots__/group_empty_state_spec.js.snap
@@ -3,6 +3,7 @@
exports[`GroupEmptyState given state BAD_QUERY passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
+ "contentClass": Array [],
"description": null,
"invertInDarkMode": true,
"primaryButtonLink": "/path/to/settings",
@@ -31,6 +32,7 @@ exports[`GroupEmptyState given state BAD_QUERY renders the slotted content 1`] =
exports[`GroupEmptyState given state CONNECTION_FAILED passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
+ "contentClass": Array [],
"description": "We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating.",
"invertInDarkMode": true,
"primaryButtonLink": "/path/to/settings",
@@ -48,6 +50,7 @@ exports[`GroupEmptyState given state CONNECTION_FAILED renders the slotted conte
exports[`GroupEmptyState given state FOO STATE passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
+ "contentClass": Array [],
"description": "An error occurred while loading the data. Please try again.",
"invertInDarkMode": true,
"primaryButtonLink": null,
@@ -65,6 +68,7 @@ exports[`GroupEmptyState given state FOO STATE renders the slotted content 1`] =
exports[`GroupEmptyState given state LOADING passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
+ "contentClass": Array [],
"description": "Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.",
"invertInDarkMode": true,
"primaryButtonLink": null,
@@ -82,6 +86,7 @@ exports[`GroupEmptyState given state LOADING renders the slotted content 1`] = `
exports[`GroupEmptyState given state NO_DATA passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
+ "contentClass": Array [],
"description": null,
"invertInDarkMode": true,
"primaryButtonLink": null,
@@ -110,6 +115,7 @@ exports[`GroupEmptyState given state NO_DATA renders the slotted content 1`] = `
exports[`GroupEmptyState given state TIMEOUT passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
+ "contentClass": Array [],
"description": null,
"invertInDarkMode": true,
"primaryButtonLink": null,
@@ -138,6 +144,7 @@ exports[`GroupEmptyState given state TIMEOUT renders the slotted content 1`] = `
exports[`GroupEmptyState given state UNKNOWN_ERROR passes the expected props to GlEmptyState 1`] = `
Object {
"compact": true,
+ "contentClass": Array [],
"description": "An error occurred while loading the data. Please try again.",
"invertInDarkMode": true,
"primaryButtonLink": null,
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index e87c4ed1eff..c4c0dc58b0d 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -9,6 +9,7 @@ import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants';
import axios from '~/lib/utils/axios_utils';
import {
HTTP_STATUS_INTERNAL_SERVER_ERROR,
+ HTTP_STATUS_OK,
HTTP_STATUS_SERVICE_UNAVAILABLE,
} from '~/lib/utils/http_status';
import * as notesConstants from '~/notes/constants';
@@ -179,7 +180,7 @@ describe('Actions Notes Store', () => {
describe('async methods', () => {
beforeEach(() => {
- axiosMock.onAny().reply(200, {});
+ axiosMock.onAny().reply(HTTP_STATUS_OK, {});
});
describe('closeMergeRequest', () => {
@@ -253,7 +254,7 @@ describe('Actions Notes Store', () => {
const pollResponse = { notes: [], last_fetched_at: '123456' };
const pollHeaders = { 'poll-interval': `${pollInterval}` };
const successMock = () =>
- axiosMock.onGet(notesDataMock.notesPath).reply(200, pollResponse, pollHeaders);
+ axiosMock.onGet(notesDataMock.notesPath).reply(HTTP_STATUS_OK, pollResponse, pollHeaders);
const failureMock = () =>
axiosMock.onGet(notesDataMock.notesPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
const advanceAndRAF = async (time) => {
@@ -350,7 +351,7 @@ describe('Actions Notes Store', () => {
.onGet(notesDataMock.notesPath)
.replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR) // cause one error
.onGet(notesDataMock.notesPath)
- .replyOnce(200, pollResponse, pollHeaders) // then a success
+ .replyOnce(HTTP_STATUS_OK, pollResponse, pollHeaders) // then a success
.onGet(notesDataMock.notesPath)
.reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); // and then more errors
@@ -403,7 +404,7 @@ describe('Actions Notes Store', () => {
const endpoint = `${TEST_HOST}/note`;
beforeEach(() => {
- axiosMock.onDelete(endpoint).replyOnce(200, {});
+ axiosMock.onDelete(endpoint).replyOnce(HTTP_STATUS_OK, {});
document.body.dataset.page = '';
});
@@ -472,7 +473,7 @@ describe('Actions Notes Store', () => {
const endpoint = `${TEST_HOST}/note`;
beforeEach(() => {
- axiosMock.onDelete(endpoint).replyOnce(200, {});
+ axiosMock.onDelete(endpoint).replyOnce(HTTP_STATUS_OK, {});
document.body.dataset.page = '';
});
@@ -512,7 +513,7 @@ describe('Actions Notes Store', () => {
};
beforeEach(() => {
- axiosMock.onAny().reply(200, res);
+ axiosMock.onAny().reply(HTTP_STATUS_OK, res);
});
it('commits ADD_NEW_NOTE and dispatches updateMergeRequestWidget', () => {
@@ -547,7 +548,7 @@ describe('Actions Notes Store', () => {
};
beforeEach(() => {
- axiosMock.onAny().replyOnce(200, res);
+ axiosMock.onAny().replyOnce(HTTP_STATUS_OK, res);
});
it('does not commit ADD_NEW_NOTE or dispatch updateMergeRequestWidget', () => {
@@ -568,7 +569,7 @@ describe('Actions Notes Store', () => {
};
beforeEach(() => {
- axiosMock.onAny().reply(200, res);
+ axiosMock.onAny().reply(HTTP_STATUS_OK, res);
});
describe('as note', () => {
@@ -759,7 +760,7 @@ describe('Actions Notes Store', () => {
it('updates discussion if response contains disussion', () => {
const discussion = { notes: [] };
- axiosMock.onAny().reply(200, { discussion });
+ axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion });
return testAction(
actions.replyToDiscussion,
@@ -778,7 +779,7 @@ describe('Actions Notes Store', () => {
it('adds a reply to a discussion', () => {
const res = {};
- axiosMock.onAny().reply(200, res);
+ axiosMock.onAny().reply(HTTP_STATUS_OK, res);
return testAction(
actions.replyToDiscussion,
@@ -1191,7 +1192,7 @@ describe('Actions Notes Store', () => {
describe('if response contains no errors', () => {
it('dispatches requestDeleteDescriptionVersion', () => {
- axiosMock.onDelete(endpoint).replyOnce(200);
+ axiosMock.onDelete(endpoint).replyOnce(HTTP_STATUS_OK);
return testAction(
actions.softDeleteDescriptionVersion,
payload,
@@ -1443,7 +1444,7 @@ describe('Actions Notes Store', () => {
});
it('updates the discussions and dispatches `updateResolvableDiscussionsCounts`', () => {
- axiosMock.onAny().reply(200, { discussion });
+ axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion });
return testAction(
actions.fetchDiscussions,
{},
@@ -1509,7 +1510,7 @@ describe('Actions Notes Store', () => {
const actionPayload = { config, path: 'test-path', perPage: 20 };
it('updates the discussions and dispatches `updateResolvableDiscussionsCounts if there are no headers', () => {
- axiosMock.onAny().reply(200, { discussion }, {});
+ axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion }, {});
return testAction(
actions.fetchDiscussionsBatch,
actionPayload,
@@ -1524,7 +1525,7 @@ describe('Actions Notes Store', () => {
});
it('dispatches itself if there is `x-next-page-cursor` header', () => {
- axiosMock.onAny().reply(200, { discussion }, { 'x-next-page-cursor': 1 });
+ axiosMock.onAny().reply(HTTP_STATUS_OK, { discussion }, { 'x-next-page-cursor': 1 });
return testAction(
actions.fetchDiscussionsBatch,
actionPayload,
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap
index a33528d2d91..801cde8582e 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/__snapshots__/packages_list_app_spec.js.snap
@@ -30,6 +30,7 @@ exports[`packages_list_app renders 1`] = `
<div
class="gl-max-w-full gl-m-auto"
+ data-testid="gl-empty-state-content"
>
<div
class="gl-mx-auto gl-my-0 gl-p-5"
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js
index 7152c237420..2c185e040f4 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/stores/actions_spec.js
@@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import Api from '~/api';
import { createAlert } from '~/flash';
-import { HTTP_STATUS_BAD_REQUEST } from '~/lib/utils/http_status';
+import { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { MISSING_DELETE_PATH_ERROR } from '~/packages_and_registries/infrastructure_registry/list/constants';
import * as actions from '~/packages_and_registries/infrastructure_registry/list/stores/actions';
import * as types from '~/packages_and_registries/infrastructure_registry/list/stores/mutation_types';
@@ -183,7 +183,7 @@ describe('Actions Package list store', () => {
},
};
it('should perform a delete operation on _links.delete_api_path', () => {
- mock.onDelete(payload._links.delete_api_path).replyOnce(200);
+ mock.onDelete(payload._links.delete_api_path).replyOnce(HTTP_STATUS_OK);
Api.projectPackages = jest.fn().mockResolvedValue({ data: 'foo' });
return testAction(
diff --git a/spec/frontend/persistent_user_callout_spec.js b/spec/frontend/persistent_user_callout_spec.js
index c9574208900..cf97d69e1c1 100644
--- a/spec/frontend/persistent_user_callout_spec.js
+++ b/spec/frontend/persistent_user_callout_spec.js
@@ -3,6 +3,7 @@ import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import PersistentUserCallout from '~/persistent_user_callout';
jest.mock('~/flash');
@@ -88,7 +89,7 @@ describe('PersistentUserCallout', () => {
${'primary'}
${'secondary'}
`('POSTs endpoint and removes container when clicking $button close', async ({ button }) => {
- mockAxios.onPost(dismissEndpoint).replyOnce(200);
+ mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK);
buttons[button].click();
@@ -140,7 +141,7 @@ describe('PersistentUserCallout', () => {
it('defers loading of a link until callout is dismissed', async () => {
const { href, target } = deferredLink;
- mockAxios.onPost(dismissEndpoint).replyOnce(200);
+ mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK);
deferredLink.click();
@@ -161,7 +162,7 @@ describe('PersistentUserCallout', () => {
});
it('does not follow link when notification is closed', async () => {
- mockAxios.onPost(dismissEndpoint).replyOnce(200);
+ mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK);
button.click();
@@ -195,7 +196,7 @@ describe('PersistentUserCallout', () => {
it('uses a link to trigger callout and defers following until callout is finished', async () => {
const { href } = link;
- mockAxios.onPost(dismissEndpoint).replyOnce(200);
+ mockAxios.onPost(dismissEndpoint).replyOnce(HTTP_STATUS_OK);
link.click();
diff --git a/spec/frontend/sidebar/sidebar_mediator_spec.js b/spec/frontend/sidebar/sidebar_mediator_spec.js
index cdb9ced70b8..77b1ccb4f9a 100644
--- a/spec/frontend/sidebar/sidebar_mediator_spec.js
+++ b/spec/frontend/sidebar/sidebar_mediator_spec.js
@@ -1,5 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import * as urlUtility from '~/lib/utils/url_utility';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarMediator from '~/sidebar/sidebar_mediator';
@@ -36,10 +37,10 @@ describe('Sidebar mediator', () => {
});
it('saves assignees', () => {
- mock.onPut(mediatorMockData.endpoint).reply(200, {});
+ mock.onPut(mediatorMockData.endpoint).reply(HTTP_STATUS_OK, {});
return mediator.saveAssignees('issue[assignee_ids]').then((resp) => {
- expect(resp.status).toEqual(200);
+ expect(resp.status).toEqual(HTTP_STATUS_OK);
});
});
@@ -91,7 +92,7 @@ describe('Sidebar mediator', () => {
it('fetches the data', async () => {
const mockData = Mock.responseMap.GET[mediatorMockData.endpoint];
- mock.onGet(mediatorMockData.endpoint).reply(200, mockData);
+ mock.onGet(mediatorMockData.endpoint).reply(HTTP_STATUS_OK, mockData);
const spy = jest.spyOn(mediator, 'processFetchedData').mockReturnValue(Promise.resolve());
await mediator.fetch();
@@ -120,7 +121,7 @@ describe('Sidebar mediator', () => {
it('fetches autocomplete projects', () => {
const searchTerm = 'foo';
- mock.onGet(mediatorMockData.projectsAutocompleteEndpoint).reply(200, {});
+ mock.onGet(mediatorMockData.projectsAutocompleteEndpoint).reply(HTTP_STATUS_OK, {});
const getterSpy = jest
.spyOn(mediator.service, 'getProjectsAutocomplete')
.mockReturnValue(Promise.resolve({ data: {} }));
@@ -137,7 +138,7 @@ describe('Sidebar mediator', () => {
it('moves issue', () => {
const mockData = Mock.responseMap.POST[mediatorMockData.moveIssueEndpoint];
const moveToProjectId = 7;
- mock.onPost(mediatorMockData.moveIssueEndpoint).reply(200, mockData);
+ mock.onPost(mediatorMockData.moveIssueEndpoint).reply(HTTP_STATUS_OK, mockData);
mediator.store.setMoveToProjectId(moveToProjectId);
const moveIssueSpy = jest
.spyOn(mediator.service, 'moveIssue')
diff --git a/spec/frontend/super_sidebar/components/counter_spec.js b/spec/frontend/super_sidebar/components/counter_spec.js
index 1150b0a3aa8..8f514540413 100644
--- a/spec/frontend/super_sidebar/components/counter_spec.js
+++ b/spec/frontend/super_sidebar/components/counter_spec.js
@@ -13,10 +13,6 @@ describe('Counter component', () => {
label: __('Issues'),
};
- afterEach(() => {
- wrapper.destroy();
- });
-
const findButton = () => wrapper.find('button');
const findIcon = () => wrapper.getComponent(GlIcon);
const findLink = () => wrapper.find('a');
diff --git a/spec/frontend/super_sidebar/components/create_menu_spec.js b/spec/frontend/super_sidebar/components/create_menu_spec.js
new file mode 100644
index 00000000000..77a3dbd67c9
--- /dev/null
+++ b/spec/frontend/super_sidebar/components/create_menu_spec.js
@@ -0,0 +1,33 @@
+import { GlDisclosureDropdown } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { __ } from '~/locale';
+import CreateMenu from '~/super_sidebar/components/create_menu.vue';
+import { createNewMenuGroups } from '../mock_data';
+
+describe('CreateMenu component', () => {
+ let wrapper;
+
+ const findGlDisclosureDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
+
+ const createWrapper = () => {
+ wrapper = shallowMountExtended(CreateMenu, {
+ propsData: {
+ groups: createNewMenuGroups,
+ },
+ });
+ };
+
+ describe('default', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it("sets the toggle's label", () => {
+ expect(findGlDisclosureDropdown().props('toggleText')).toBe(__('Create new...'));
+ });
+
+ it('passes the groups to the disclosure dropdown', () => {
+ expect(findGlDisclosureDropdown().props('items')).toBe(createNewMenuGroups);
+ });
+ });
+});
diff --git a/spec/frontend/super_sidebar/components/super_sidebar_spec.js b/spec/frontend/super_sidebar/components/super_sidebar_spec.js
index d7d2f67dc8a..86ba1c1ea45 100644
--- a/spec/frontend/super_sidebar/components/super_sidebar_spec.js
+++ b/spec/frontend/super_sidebar/components/super_sidebar_spec.js
@@ -8,10 +8,6 @@ describe('SuperSidebar component', () => {
const findUserBar = () => wrapper.findComponent(UserBar);
- afterEach(() => {
- wrapper.destroy();
- });
-
const createWrapper = (props = {}) => {
wrapper = shallowMountExtended(SuperSidebar, {
propsData: {
diff --git a/spec/frontend/super_sidebar/components/user_bar_spec.js b/spec/frontend/super_sidebar/components/user_bar_spec.js
index 6d0186a2749..d7e658a1451 100644
--- a/spec/frontend/super_sidebar/components/user_bar_spec.js
+++ b/spec/frontend/super_sidebar/components/user_bar_spec.js
@@ -1,5 +1,6 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { __ } from '~/locale';
+import CreateMenu from '~/super_sidebar/components/create_menu.vue';
import Counter from '~/super_sidebar/components/counter.vue';
import UserBar from '~/super_sidebar/components/user_bar.vue';
import { sidebarData } from '../mock_data';
@@ -7,12 +8,9 @@ import { sidebarData } from '../mock_data';
describe('UserBar component', () => {
let wrapper;
+ const findCreateMenu = () => wrapper.findComponent(CreateMenu);
const findCounter = (at) => wrapper.findAllComponents(Counter).at(at);
- afterEach(() => {
- wrapper.destroy();
- });
-
const createWrapper = (props = {}) => {
wrapper = shallowMountExtended(UserBar, {
propsData: {
@@ -31,6 +29,10 @@ describe('UserBar component', () => {
createWrapper();
});
+ it('passes the "Create new..." menu groups to the create-menu component', () => {
+ expect(findCreateMenu().props('groups')).toBe(sidebarData.create_new_menu_groups);
+ });
+
it('renders issues counter', () => {
expect(findCounter(0).props('count')).toBe(sidebarData.assigned_open_issues_count);
expect(findCounter(0).props('href')).toBe(sidebarData.issues_dashboard_path);
diff --git a/spec/frontend/super_sidebar/mock_data.js b/spec/frontend/super_sidebar/mock_data.js
index 7db0d0ea5cc..bdbc25e49f0 100644
--- a/spec/frontend/super_sidebar/mock_data.js
+++ b/spec/frontend/super_sidebar/mock_data.js
@@ -1,3 +1,44 @@
+export const createNewMenuGroups = [
+ {
+ name: 'This group',
+ items: [
+ {
+ text: 'New project/repository',
+ href: '/projects/new?namespace_id=22',
+ },
+ {
+ text: 'New subgroup',
+ href: '/groups/new?parent_id=22#create-group-pane',
+ },
+ {
+ text: 'New epic',
+ href: '/groups/gitlab-org/-/epics/new',
+ },
+ {
+ text: 'Invite members',
+ href: '/groups/gitlab-org/-/group_members',
+ },
+ ],
+ },
+ {
+ name: 'GitLab',
+ items: [
+ {
+ text: 'New project/repository',
+ href: '/projects/new',
+ },
+ {
+ text: 'New group',
+ href: '/groups/new',
+ },
+ {
+ text: 'New snippet',
+ href: '/-/snippets/new',
+ },
+ ],
+ },
+];
+
export const sidebarData = {
name: 'Administrator',
username: 'root',
@@ -6,4 +47,5 @@ export const sidebarData = {
assigned_open_merge_requests_count: 2,
todos_pending_count: 3,
issues_dashboard_path: 'path/to/issues',
+ create_new_menu_groups: createNewMenuGroups,
};
diff --git a/spec/frontend/vue_shared/components/dismissible_container_spec.js b/spec/frontend/vue_shared/components/dismissible_container_spec.js
index f7030f38709..7d8581e11e9 100644
--- a/spec/frontend/vue_shared/components/dismissible_container_spec.js
+++ b/spec/frontend/vue_shared/components/dismissible_container_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import dismissibleContainer from '~/vue_shared/components/dismissible_container.vue';
describe('DismissibleContainer', () => {
@@ -28,7 +29,7 @@ describe('DismissibleContainer', () => {
});
it('successfully dismisses', () => {
- mockAxios.onPost(propsData.path).replyOnce(200);
+ mockAxios.onPost(propsData.path).replyOnce(HTTP_STATUS_OK);
const button = findBtn();
button.trigger('click');
diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb
index f2a53b373ac..714eaebfe73 100644
--- a/spec/graphql/types/ci/job_type_spec.rb
+++ b/spec/graphql/types/ci/job_type_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe Types::Ci::JobType do
detailedStatus
duration
downstreamPipeline
+ erasedAt
finished_at
id
kind
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index a8514c373db..f628651b4da 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -211,7 +211,7 @@ RSpec.describe ApplicationHelper do
describe '#support_url' do
context 'when alternate support url is specified' do
- let(:alternate_url) { 'http://company.example.com/getting-help' }
+ let(:alternate_url) { 'http://company.example.com/get-help' }
it 'returns the alternate support url' do
stub_application_setting(help_page_support_url: alternate_url)
@@ -222,7 +222,7 @@ RSpec.describe ApplicationHelper do
context 'when alternate support url is not specified' do
it 'builds the support url from the promo_url' do
- expect(helper.support_url).to eq(helper.promo_url + '/getting-help/')
+ expect(helper.support_url).to eq(helper.promo_url + '/get-help/')
end
end
end
diff --git a/spec/helpers/nav/new_dropdown_helper_spec.rb b/spec/helpers/nav/new_dropdown_helper_spec.rb
index 3a65131aab0..b325a0ba228 100644
--- a/spec/helpers/nav/new_dropdown_helper_spec.rb
+++ b/spec/helpers/nav/new_dropdown_helper_spec.rb
@@ -77,7 +77,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'has project menu item' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: _('GitLab'),
+ title: _('In GitLab'),
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'general_new_project',
title: 'New project/repository',
@@ -95,7 +95,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'has group menu item' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: _('GitLab'),
+ title: _('In GitLab'),
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'general_new_group',
title: 'New group',
@@ -113,7 +113,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'has new snippet menu item' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: _('GitLab'),
+ title: _('In GitLab'),
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'general_new_snippet',
title: 'New snippet',
@@ -151,7 +151,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'has new project menu item' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: 'This group',
+ title: 'In this group',
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'new_project',
title: 'New project/repository',
@@ -169,7 +169,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'has new subgroup menu item' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: 'This group',
+ title: 'In this group',
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'new_subgroup',
title: 'New subgroup',
@@ -184,7 +184,7 @@ RSpec.describe Nav::NewDropdownHelper do
context 'when can invite members' do
let(:with_can_admin_in_group) { true }
let(:with_invite_members_experiment) { true }
- let(:expected_title) { 'This group' }
+ let(:expected_title) { 'In this group' }
let(:expected_href) { "/groups/#{group.full_path}/-/group_members" }
it_behaves_like 'invite member link shared example'
@@ -218,7 +218,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'shows new issue menu item' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: 'This project',
+ title: 'In this project',
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'new_issue',
title: 'New issue',
@@ -236,7 +236,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'shows merge project' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: 'This project',
+ title: 'In this project',
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'new_mr',
title: 'New merge request',
@@ -254,7 +254,7 @@ RSpec.describe Nav::NewDropdownHelper do
it 'shows new snippet' do
expect(subject[:menu_sections]).to eq(
expected_menu_section(
- title: 'This project',
+ title: 'In this project',
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'new_snippet',
title: 'New snippet',
@@ -269,7 +269,7 @@ RSpec.describe Nav::NewDropdownHelper do
context 'when invite members experiment' do
let(:with_invite_members_experiment) { true }
let(:with_can_admin_project_member) { true }
- let(:expected_title) { 'This project' }
+ let(:expected_title) { 'In this project' }
let(:expected_href) { "/#{project.path_with_namespace}/-/project_members" }
it_behaves_like 'invite member link shared example'
diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb
index 299e4cb0133..fcaff83ada4 100644
--- a/spec/helpers/sidebars_helper_spec.rb
+++ b/spec/helpers/sidebars_helper_spec.rb
@@ -47,15 +47,19 @@ RSpec.describe SidebarsHelper do
describe '#super_sidebar_context' do
let(:user) { build(:user) }
+ let(:group) { build(:group) }
- subject { helper.super_sidebar_context(user) }
+ subject { helper.super_sidebar_context(user, group: group, project: nil) }
- it 'returns sidebar values from user', :use_clean_rails_memory_store_caching do
+ before do
+ allow(helper).to receive(:current_user) { user }
Rails.cache.write(['users', user.id, 'assigned_open_issues_count'], 1)
Rails.cache.write(['users', user.id, 'assigned_open_merge_requests_count'], 2)
Rails.cache.write(['users', user.id, 'todos_pending_count'], 3)
+ end
- expect(subject).to eq({
+ it 'returns sidebar values from user', :use_clean_rails_memory_store_caching do
+ expect(subject).to include({
name: user.name,
username: user.username,
avatar_url: user.avatar_url,
@@ -65,5 +69,42 @@ RSpec.describe SidebarsHelper do
issues_dashboard_path: issues_dashboard_path(assignee_username: user.username)
})
end
+
+ it 'returns "Create new" menu groups without headers', :use_clean_rails_memory_store_caching do
+ expect(subject[:create_new_menu_groups]).to eq([
+ {
+ name: "",
+ items: [
+ { href: "/projects/new", text: "New project/repository" },
+ { href: "/groups/new", text: "New group" },
+ { href: "/-/snippets/new", text: "New snippet" }
+ ]
+ }
+ ])
+ end
+
+ it 'returns "Create new" menu groups with headers', :use_clean_rails_memory_store_caching do
+ allow(group).to receive(:persisted?).and_return(true)
+ allow(helper).to receive(:can?).and_return(true)
+
+ expect(subject[:create_new_menu_groups]).to contain_exactly(
+ a_hash_including(
+ name: "In this group",
+ items: array_including(
+ { href: "/projects/new", text: "New project/repository" },
+ { href: "/groups/new#create-group-pane", text: "New subgroup" },
+ { href: "/groups/#{group.full_path}/-/group_members", text: "Invite members" }
+ )
+ ),
+ a_hash_including(
+ name: "In GitLab",
+ items: array_including(
+ { href: "/projects/new", text: "New project/repository" },
+ { href: "/groups/new", text: "New group" },
+ { href: "/-/snippets/new", text: "New snippet" }
+ )
+ )
+ )
+ end
end
end
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index 875bfc5b94f..539768108cb 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -126,6 +126,7 @@ RSpec.describe API::Ci::Jobs, feature_category: :continuous_integration do
it 'returns specific job data' do
expect(json_response['finished_at']).to be_nil
+ expect(json_response['erased_at']).to be_nil
end
it 'avoids N+1 queries', :skip_before_request do
@@ -651,6 +652,18 @@ RSpec.describe API::Ci::Jobs, feature_category: :continuous_integration do
end
end
+ context 'when job is erased' do
+ let(:job) do
+ create(:ci_build, pipeline: pipeline, erased_at: Time.now)
+ end
+
+ it 'returns specific job data' do
+ get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
+
+ expect(Time.parse(json_response['erased_at'])).to be_like_time(job.erased_at)
+ end
+ end
+
context 'when trace artifact record exists with no stored file', :skip_before_request do
before do
create(:ci_job_artifact, :unarchived_trace_artifact, job: job, project: job.project)
diff --git a/spec/support/shared_examples/integrations/integration_settings_form.rb b/spec/support/shared_examples/integrations/integration_settings_form.rb
index 5041ac4a660..5eeefdf7e50 100644
--- a/spec/support/shared_examples/integrations/integration_settings_form.rb
+++ b/spec/support/shared_examples/integrations/integration_settings_form.rb
@@ -7,6 +7,7 @@ RSpec.shared_examples 'integration settings form' do
it 'displays all the integrations' do
aggregate_failures do
integrations.each do |integration|
+ stub_feature_flags(integration_slack_app_notifications: false)
navigate_to_integration(integration)
page.within('form.integration-settings-form') do
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index 7671c65d22c..ea3201dd44a 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require 'rake'
-RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
+RSpec.describe 'gitlab:db namespace rake task', :silence_stdout, feature_category: :database do
before :all do
Rake.application.rake_require 'active_record/railties/databases'
Rake.application.rake_require 'tasks/seed_fu'
@@ -352,6 +352,94 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
end
end
+ describe 'dictionary generate' do
+ let(:db_config) { instance_double(ActiveRecord::DatabaseConfigurations::HashConfig, name: 'fake_db') }
+
+ let(:model) { ActiveRecord::Base }
+ let(:connection) { model.connection }
+
+ let(:base_models) { { 'fake_db' => model }.with_indifferent_access }
+
+ let(:tables) { %w[table1] }
+ let(:views) { %w[view1] }
+
+ let(:table_file_path) { 'db/docs/table1.yml' }
+ let(:view_file_path) { 'db/docs/views/view1.yml' }
+
+ before do
+ allow(Gitlab::Database).to receive(:db_config_for_connection).and_return(db_config)
+ allow(Gitlab::Database).to receive(:database_base_models).and_return(base_models)
+
+ allow(connection).to receive(:tables).and_return(tables)
+ allow(connection).to receive(:views).and_return(views)
+ end
+
+ after do
+ File.delete(table_file_path)
+ File.delete(view_file_path)
+ end
+
+ context 'when the dictionary files do not exist' do
+ it 'generate the dictionary files' do
+ run_rake_task('gitlab:db:dictionary:generate')
+
+ expect(File).to exist(File.join(table_file_path))
+ expect(File).to exist(File.join(view_file_path))
+ end
+ end
+
+ context 'when the dictionary files already exist' do
+ let(:table_class) do
+ Class.new(ApplicationRecord) do
+ self.table_name = 'table1'
+ end
+ end
+
+ let(:view_class) do
+ Class.new(ApplicationRecord) do
+ self.table_name = 'view1'
+ end
+ end
+
+ table_metadata = {
+ 'table_name' => 'table1',
+ 'classes' => [],
+ 'feature_categories' => [],
+ 'description' => nil,
+ 'introduced_by_url' => nil,
+ 'milestone' => 14.3
+ }
+ view_metadata = {
+ 'view_name' => 'view1',
+ 'classes' => [],
+ 'feature_categories' => [],
+ 'description' => nil,
+ 'introduced_by_url' => nil,
+ 'milestone' => 14.3
+ }
+
+ before do
+ stub_const('TableClass', table_class)
+ stub_const('ViewClass', view_class)
+
+ File.write(table_file_path, table_metadata.to_yaml)
+ File.write(view_file_path, view_metadata.to_yaml)
+
+ allow(model).to receive(:descendants).and_return([table_class, view_class])
+ end
+
+ it 'update the dictionary content' do
+ run_rake_task('gitlab:db:dictionary:generate')
+
+ table_metadata = YAML.safe_load(File.read(table_file_path))
+ expect(table_metadata['classes']).to match_array(['TableClass'])
+
+ view_metadata = YAML.safe_load(File.read(view_file_path))
+ expect(view_metadata['classes']).to match_array(['ViewClass'])
+ end
+ end
+ end
+
describe 'unattended' do
using RSpec::Parameterized::TableSyntax
diff --git a/yarn.lock b/yarn.lock
index 9608180e26d..94da9aa70fc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1140,15 +1140,15 @@
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.2.0"
-"@gitlab/svgs@3.16.0":
- version "3.16.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.16.0.tgz#ba7f566c160bd408f3fce63b92bbf9d66a0e6a4d"
- integrity sha512-sL2D6yNxq4eT5go6d9k9QBbYyvlqWbxWUFB+Ty0/xxxx1ZaWlBs/PpGERgebPZ9uk5v4FIW1mI+Ra8ZVFBOjww==
-
-"@gitlab/ui@52.11.0":
- version "52.11.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-52.11.0.tgz#a81f5e4901531fca3d2340cc17f1282d2370665c"
- integrity sha512-6ocP2GhIpaq4+Zf05n1nBoTckyHEYGf9KRs0CNyOyNoCe28fSTRX91KTdC4hcvam9IdQZY049X+tcG6bRxooCg==
+"@gitlab/svgs@3.17.0":
+ version "3.17.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.17.0.tgz#beeda4bd2b97ec2637bebe1760dbe283d6a599ef"
+ integrity sha512-+5wsh/FG7SSkUQjehROl+0nRgrg/XRNUa9h3LkxpksP0AXy4j6xuYuq+7xucDGJDdXo43tUftLc9w7u/SCmgQA==
+
+"@gitlab/ui@52.13.1":
+ version "52.13.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-52.13.1.tgz#178854d01de1637240e60d06572ff01fc927bc5a"
+ integrity sha512-TSS5ghAto0ZPGeVmucrrYgvfBBhesvvKm4wPz29MkQRv3yCUbTJ+emBgXLDECDvUZvZPfwomN6QqsyQzVezz5g==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"