Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f605b80ff7
commit
1c00bf7781
|
|
@ -201,7 +201,7 @@ export default {
|
|||
data-testid="group-name"
|
||||
:href="group.relativePath"
|
||||
:title="group.fullName"
|
||||
class="no-expand gl-mr-3 gl-text-gray-900!"
|
||||
class="no-expand gl-mr-3 gl-text-gray-900! gl-word-break-word"
|
||||
:itemprop="microdata.nameItemprop"
|
||||
>
|
||||
<!-- ending bracket must be by closing tag to prevent -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
<script>
|
||||
import { GlDisclosureDropdownGroup } from '@gitlab/ui';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import { ALL_GITLAB } from '~/vue_shared/global_search/constants';
|
||||
|
||||
export default {
|
||||
name: 'DefaultIssuables',
|
||||
i18n: {
|
||||
ALL_GITLAB,
|
||||
},
|
||||
components: {
|
||||
GlDisclosureDropdownGroup,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['searchContext']),
|
||||
...mapGetters(['defaultSearchOptions']),
|
||||
currentContextName() {
|
||||
return (
|
||||
this.searchContext?.project?.name ||
|
||||
this.searchContext?.group?.name ||
|
||||
this.$options.i18n.ALL_GITLAB
|
||||
);
|
||||
},
|
||||
shouldRender() {
|
||||
return this.group.items.length > 0;
|
||||
},
|
||||
group() {
|
||||
return {
|
||||
name: this.currentContextName,
|
||||
items: this.defaultSearchOptions,
|
||||
};
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (!this.shouldRender) {
|
||||
this.$emit('nothing-to-render');
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-disclosure-dropdown-group v-if="shouldRender" v-bind="$attrs" :group="group" />
|
||||
</template>
|
||||
|
|
@ -1,54 +1,51 @@
|
|||
<script>
|
||||
import { GlDisclosureDropdownGroup } from '@gitlab/ui';
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import { ALL_GITLAB, PLACES } from '~/vue_shared/global_search/constants';
|
||||
import DefaultPlaces from './global_search_default_places.vue';
|
||||
import DefaultIssuables from './global_search_default_issuables.vue';
|
||||
|
||||
const components = [DefaultPlaces, DefaultIssuables];
|
||||
|
||||
export default {
|
||||
name: 'GlobalSearchDefaultItems',
|
||||
i18n: {
|
||||
ALL_GITLAB,
|
||||
PLACES,
|
||||
data() {
|
||||
return {
|
||||
// The components here are expected to:
|
||||
// - be responsible for getting their own data,
|
||||
// - render a GlDisclosureDropdownGroup as the root vnode,
|
||||
// - transparently pass all attrs to it (e.g., `bordered`),
|
||||
// - not render anything if they have no data,
|
||||
// - emit a `nothing-to-render` event if they have nothing to render.
|
||||
// - have a unique `name`
|
||||
componentNames: components.map(({ name }) => name),
|
||||
};
|
||||
},
|
||||
components: {
|
||||
GlDisclosureDropdownGroup,
|
||||
},
|
||||
inject: ['contextSwitcherLinks'],
|
||||
computed: {
|
||||
...mapState(['searchContext']),
|
||||
...mapGetters(['defaultSearchOptions']),
|
||||
currentContextName() {
|
||||
return (
|
||||
this.searchContext?.project?.name ||
|
||||
this.searchContext?.group?.name ||
|
||||
this.$options.i18n.ALL_GITLAB
|
||||
);
|
||||
methods: {
|
||||
componentFromName(name) {
|
||||
return components.find((component) => component.name === name);
|
||||
},
|
||||
groups() {
|
||||
const groups = [
|
||||
{
|
||||
name: this.$options.i18n.PLACES,
|
||||
items: this.contextSwitcherLinks.map(({ title, link }) => ({ text: title, href: link })),
|
||||
},
|
||||
{
|
||||
name: this.currentContextName,
|
||||
items: this.defaultSearchOptions,
|
||||
},
|
||||
];
|
||||
|
||||
return groups.filter(({ items }) => items.length > 0);
|
||||
remove(nameToRemove) {
|
||||
const indexToRemove = this.componentNames.findIndex((name) => name === nameToRemove);
|
||||
if (indexToRemove !== -1) this.componentNames.splice(indexToRemove, 1);
|
||||
},
|
||||
attrs(index) {
|
||||
return index === 0
|
||||
? null
|
||||
: {
|
||||
bordered: true,
|
||||
class: 'gl-mt-3',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul class="gl-p-0 gl-m-0 gl-list-style-none">
|
||||
<gl-disclosure-dropdown-group
|
||||
v-for="(group, index) of groups"
|
||||
:key="group.name"
|
||||
:group="group"
|
||||
bordered
|
||||
:class="{ 'gl-mt-0!': index === 0 }"
|
||||
<ul class="gl-p-0 gl-m-0 gl-pt-2 gl-list-style-none">
|
||||
<component
|
||||
:is="componentFromName(name)"
|
||||
v-for="(name, index) in componentNames"
|
||||
:key="name"
|
||||
v-bind="attrs(index)"
|
||||
@nothing-to-render="remove(name)"
|
||||
/>
|
||||
</ul>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
<script>
|
||||
import { GlDisclosureDropdownGroup } from '@gitlab/ui';
|
||||
import { PLACES } from '~/vue_shared/global_search/constants';
|
||||
|
||||
export default {
|
||||
name: 'DefaultPlaces',
|
||||
i18n: {
|
||||
PLACES,
|
||||
},
|
||||
components: {
|
||||
GlDisclosureDropdownGroup,
|
||||
},
|
||||
inject: ['contextSwitcherLinks'],
|
||||
computed: {
|
||||
shouldRender() {
|
||||
return this.contextSwitcherLinks.length > 0;
|
||||
},
|
||||
group() {
|
||||
return {
|
||||
name: this.$options.i18n.PLACES,
|
||||
items: this.contextSwitcherLinks.map(({ title, link }) => ({ text: title, href: link })),
|
||||
};
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (!this.shouldRender) {
|
||||
this.$emit('nothing-to-render');
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-disclosure-dropdown-group v-if="shouldRender" v-bind="$attrs" :group="group" />
|
||||
</template>
|
||||
|
|
@ -493,31 +493,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.protected-branches-list {
|
||||
margin-bottom: 32px;
|
||||
|
||||
.settings-message {
|
||||
margin: 0;
|
||||
border-radius: 0 0 1px 1px;
|
||||
padding: 20px 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.table-bordered {
|
||||
border-radius: 1px;
|
||||
|
||||
th:not(:last-child),
|
||||
td:not(:last-child) {
|
||||
border-right: solid 1px transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.flash-container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.compare-revision-cards {
|
||||
@media (max-width: $breakpoint-lg) {
|
||||
.swap-button {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,22 @@ class CustomerRelations::Contact < ApplicationRecord
|
|||
scope :order_by_organization_asc, -> { includes(:organization).order("customer_relations_organizations.name ASC NULLS LAST") }
|
||||
scope :order_by_organization_desc, -> { includes(:organization).order("customer_relations_organizations.name DESC NULLS LAST") }
|
||||
|
||||
SAFE_ATTRIBUTES = %w[
|
||||
created_at
|
||||
description
|
||||
first_name
|
||||
group_id
|
||||
id
|
||||
last_name
|
||||
organization_id
|
||||
state
|
||||
updated_at
|
||||
].freeze
|
||||
|
||||
def hook_attrs
|
||||
attributes.slice(*SAFE_ATTRIBUTES)
|
||||
end
|
||||
|
||||
def self.reference_prefix
|
||||
'[contact:'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -770,7 +770,7 @@ class Issue < ApplicationRecord
|
|||
return unless persisted?
|
||||
|
||||
if confidential? && WorkItems::ParentLink.has_public_children?(id)
|
||||
errors.add(:base, _('A confidential issue cannot have a parent that already has non-confidential children.'))
|
||||
errors.add(:base, _('A confidential issue must have only confidential children. Make any child items confidential and try again.'))
|
||||
end
|
||||
|
||||
if !confidential? && WorkItems::ParentLink.has_confidential_parent?(id)
|
||||
|
|
|
|||
|
|
@ -1166,7 +1166,7 @@ class MergeRequest < ApplicationRecord
|
|||
MergeRequests::ReloadDiffsService.new(self, current_user).execute
|
||||
end
|
||||
|
||||
def check_mergeability(async: false)
|
||||
def check_mergeability(async: false, sync_retry_lease: false)
|
||||
return unless recheck_merge_status?
|
||||
|
||||
check_service = MergeRequests::MergeabilityCheckService.new(self)
|
||||
|
|
@ -1174,7 +1174,7 @@ class MergeRequest < ApplicationRecord
|
|||
if async
|
||||
check_service.async_execute
|
||||
else
|
||||
check_service.execute(retry_lease: false)
|
||||
check_service.execute(retry_lease: sync_retry_lease)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ServiceClass
|
||||
|
|
@ -1218,14 +1218,14 @@ class MergeRequest < ApplicationRecord
|
|||
}
|
||||
end
|
||||
|
||||
def mergeable?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false)
|
||||
def mergeable?(skip_ci_check: false, skip_discussions_check: false, skip_approved_check: false, check_mergeability_retry_lease: false)
|
||||
return false unless mergeable_state?(
|
||||
skip_ci_check: skip_ci_check,
|
||||
skip_discussions_check: skip_discussions_check,
|
||||
skip_approved_check: skip_approved_check
|
||||
)
|
||||
|
||||
check_mergeability
|
||||
check_mergeability(sync_retry_lease: check_mergeability_retry_lease)
|
||||
|
||||
can_be_merged? && !should_be_rebased?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ module MergeRequests
|
|||
error =
|
||||
if @merge_request.should_be_rebased?
|
||||
'Only fast-forward merge is allowed for your project. Please update your source branch'
|
||||
elsif !@merge_request.mergeable?(skip_discussions_check: @options[:skip_discussions_check])
|
||||
elsif !@merge_request.mergeable?(skip_discussions_check: @options[:skip_discussions_check], check_mergeability_retry_lease: @options[:check_mergeability_retry_lease])
|
||||
'Merge request is not mergeable'
|
||||
elsif !@merge_request.squash && project.squash_always?
|
||||
'This project requires squashing commits when merge requests are accepted.'
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
.avatar-container.rect-avatar.s64.home-panel-avatar.gl-flex-shrink-0.float-none{ class: 'gl-mr-3!' }
|
||||
= group_icon(@group, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'logo')
|
||||
%div
|
||||
%h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2.gl-display-flex{ itemprop: 'name' }
|
||||
%h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2.gl-display-flex.gl-word-break-word{ itemprop: 'name' }
|
||||
= @group.name
|
||||
%span.visibility-icon.gl-text-secondary.has-tooltip.gl-ml-2{ data: { container: 'body' }, title: visibility_icon_description(@group) }
|
||||
= visibility_level_icon(@group.visibility_level, options: {class: 'icon'})
|
||||
|
|
|
|||
|
|
@ -46,4 +46,4 @@
|
|||
%td= instance_configuration_human_size_cell(size_limits[:max_import_remote_file_size])
|
||||
%tr
|
||||
%td= s_('BulkImport|Direct transfer maximum download file size (MB)')
|
||||
%td= instance_configuration_human_size_cell(size_limits[:max_import_remote_file_size])
|
||||
%td= instance_configuration_human_size_cell(size_limits[:bulk_import_max_download_file_size])
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
%div{ class: 'avatar-container rect-avatar s64 home-panel-avatar gl-flex-shrink-0 gl-w-11 gl-h-11 gl-mr-3! float-none' }
|
||||
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'image')
|
||||
%div
|
||||
%h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2.gl-display-flex{ data: { qa_selector: 'project_name_content' }, itemprop: 'name' }
|
||||
%h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2.gl-display-flex.gl-word-break-word{ data: { qa_selector: 'project_name_content' }, itemprop: 'name' }
|
||||
= @project.name
|
||||
= visibility_level_content(@project, css_class: 'visibility-icon gl-text-secondary gl-ml-2', icon_css_class: 'icon')
|
||||
= render_if_exists 'compliance_management/compliance_framework/compliance_framework_badge', project: @project, additional_classes: 'gl-align-self-center gl-ml-2'
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
%span.js-mirrored-repo-count
|
||||
= mirrored_repositories_count
|
||||
.gl-new-card-actions
|
||||
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content" }) do
|
||||
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content", data: { testid: 'add-new-mirror' } }) do
|
||||
= _('Add new')
|
||||
- c.with_body do
|
||||
- if mirror_settings_enabled
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
.protected-branches-list.js-protected-branches-list{ data: { testid: 'protected-branches-list' } }
|
||||
- if @protected_branches.empty?
|
||||
.card-header.bg-white
|
||||
= s_("ProtectedBranch|Protected branch (%{protected_branches_count})") % { protected_branches_count: 0 }
|
||||
%p.settings-message.text-center
|
||||
= s_("ProtectedBranch|There are currently no protected branches, protect a branch with the form above.")
|
||||
%p.gl-new-card-empty.gl-px-5.gl-py-4.js-toggle-content
|
||||
= s_("ProtectedBranch|There are currently no protected branches, to protect a branch start by creating a new one above.")
|
||||
- else
|
||||
.flash-container
|
||||
%table.table.table-bordered
|
||||
%table.table.b-table.gl-table.b-table-stacked-md
|
||||
%colgroup
|
||||
%col{ width: "30%" }
|
||||
%col{ width: "20%" }
|
||||
|
|
@ -34,5 +32,3 @@
|
|||
%th
|
||||
%tbody
|
||||
= yield
|
||||
|
||||
= paginate @protected_branches, theme: 'gitlab'
|
||||
|
|
|
|||
|
|
@ -1,44 +1,43 @@
|
|||
= gitlab_ui_form_for [protected_branch_entity, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f|
|
||||
%input{ type: 'hidden', name: 'update_section', value: 'js-protected-branches-settings' }
|
||||
= render Pajamas::CardComponent.new(card_options: { class: "gl-mb-5" }) do |c|
|
||||
- c.with_header do
|
||||
= s_("ProtectedBranch|Protect a branch")
|
||||
- c.with_body do
|
||||
= form_errors(@protected_branch)
|
||||
.form-group.row
|
||||
= f.label :name, s_('ProtectedBranch|Branch:'), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
- if protected_branch_entity.is_a?(Group)
|
||||
= f.text_field :name, placeholder: 'prod*', class: 'form-control gl-w-full! gl-form-input-lg'
|
||||
- else
|
||||
= render partial: "protected_branches/shared/dropdown", locals: { f: f, toggle_classes: 'gl-w-full! gl-form-input-lg' }
|
||||
.form-text.text-muted
|
||||
- wildcards_url = help_page_url('user/project/protected_branches', anchor: 'protect-multiple-branches-with-wildcard-rules')
|
||||
- wildcards_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: wildcards_url }
|
||||
- placeholders = { wildcards_link_start: wildcards_link_start, wildcards_link_end: '</a>', code_tag_start: '<code>', code_tag_end: '</code>' }
|
||||
- if protected_branch_entity.is_a?(Group)
|
||||
= (s_("ProtectedBranch|Only %{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported.") % placeholders).html_safe
|
||||
- else
|
||||
= (s_("ProtectedBranch|%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported.") % placeholders).html_safe
|
||||
.form-group.row
|
||||
= f.label :merge_access_levels_attributes, s_("ProtectedBranch|Allowed to merge:"), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
= yield :merge_access_levels
|
||||
.form-group.row
|
||||
= f.label :push_access_levels_attributes, s_("ProtectedBranch|Allowed to push and merge:"), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
= yield :push_access_levels
|
||||
.form-group.row
|
||||
= f.label :allow_force_push, s_("ProtectedBranch|Allowed to force push:"), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
= render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle',
|
||||
label: s_("ProtectedBranch|Allowed to force push"),
|
||||
label_position: :hidden) do
|
||||
- force_push_docs_url = help_page_url('topics/git/git_rebase', anchor: 'force-push')
|
||||
- force_push_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: force_push_docs_url }
|
||||
= (s_("ProtectedBranch|Allow all users with push access to %{tag_start}force push%{tag_end}.") % { tag_start: force_push_link_start, tag_end: '</a>' }).html_safe
|
||||
= render_if_exists 'protected_branches/ee/code_owner_approval_form', f: f, protected_branch_entity: protected_branch_entity
|
||||
- c.with_footer do
|
||||
= f.submit s_('ProtectedBranch|Protect'), disabled: true, data: { qa_selector: 'protect_button' }, pajamas_button: true
|
||||
|
||||
.js-alert-protected-branch-created-container.gl-mb-5
|
||||
= form_errors(@protected_branch)
|
||||
|
||||
%h4.gl-mt-0= s_("ProtectedBranch|Protect a branch")
|
||||
|
||||
.form-group.row
|
||||
= f.label :name, s_('ProtectedBranch|Branch:'), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
- if protected_branch_entity.is_a?(Group)
|
||||
= f.text_field :name, placeholder: 'prod*', class: 'form-control gl-w-full! gl-form-input-lg'
|
||||
- else
|
||||
= render partial: "protected_branches/shared/dropdown", locals: { f: f, toggle_classes: 'gl-w-full! gl-form-input-lg' }
|
||||
.form-text.text-muted
|
||||
- wildcards_url = help_page_url('user/project/protected_branches', anchor: 'protect-multiple-branches-with-wildcard-rules')
|
||||
- wildcards_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: wildcards_url }
|
||||
- placeholders = { wildcards_link_start: wildcards_link_start, wildcards_link_end: '</a>', code_tag_start: '<code>', code_tag_end: '</code>' }
|
||||
- if protected_branch_entity.is_a?(Group)
|
||||
= (s_("ProtectedBranch|Only %{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported.") % placeholders).html_safe
|
||||
- else
|
||||
= (s_("ProtectedBranch|%{wildcards_link_start}Wildcards%{wildcards_link_end} such as %{code_tag_start}*-stable%{code_tag_end} or %{code_tag_start}production/*%{code_tag_end} are supported.") % placeholders).html_safe
|
||||
.form-group.row
|
||||
= f.label :merge_access_levels_attributes, s_("ProtectedBranch|Allowed to merge:"), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
= yield :merge_access_levels
|
||||
.form-group.row
|
||||
= f.label :push_access_levels_attributes, s_("ProtectedBranch|Allowed to push and merge:"), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
= yield :push_access_levels
|
||||
.form-group.row
|
||||
= f.label :allow_force_push, s_("ProtectedBranch|Allowed to force push:"), class: 'col-sm-12'
|
||||
.col-sm-12
|
||||
= render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle',
|
||||
label: s_("ProtectedBranch|Allowed to force push"),
|
||||
label_position: :hidden) do
|
||||
- force_push_docs_url = help_page_url('topics/git/git_rebase', anchor: 'force-push')
|
||||
- force_push_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: force_push_docs_url }
|
||||
= (s_("ProtectedBranch|Allow all users with push access to %{tag_start}force push%{tag_end}.") % { tag_start: force_push_link_start, tag_end: '</a>' }).html_safe
|
||||
= render_if_exists 'protected_branches/ee/code_owner_approval_form', f: f, protected_branch_entity: protected_branch_entity
|
||||
= f.submit s_('ProtectedBranch|Protect'), disabled: true, data: { qa_selector: 'protect_button' }, pajamas_button: true
|
||||
= render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-2 js-toggle-button' }) do
|
||||
= _('Cancel')
|
||||
|
|
|
|||
|
|
@ -7,15 +7,31 @@
|
|||
= s_("ProtectedBranch|Protected branches")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p
|
||||
%p.gl-text-secondary.gl-mb-0
|
||||
= s_("ProtectedBranch|Keep stable branches secure and force developers to use merge requests.")
|
||||
= link_to s_("ProtectedBranch|What are protected branches?"), help_page_path("user/project/protected_branches")
|
||||
.settings-content
|
||||
%p
|
||||
= s_("ProtectedBranch|By default, protected branches restrict who can modify the branch.")
|
||||
= link_to s_("ProtectedBranch|Learn more."), help_page_path("user/project/protected_branches", anchor: "who-can-modify-a-protected-branch")
|
||||
.js-alert-protected-branch-created-container.gl-mt-5
|
||||
|
||||
- if can_admin_entity
|
||||
= content_for :create_protected_branch
|
||||
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
|
||||
- c.with_header do
|
||||
.gl-new-card-title-wrapper.gl-justify-content-space-between
|
||||
%h3.gl-new-card-title
|
||||
= s_("ProtectedBranch|Protected branches")
|
||||
.gl-new-card-count
|
||||
= sprite_icon('branch', css_class: 'gl-mr-2')
|
||||
%span= @protected_branches.size
|
||||
.gl-new-card-actions
|
||||
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'add-new-token-button' } }) do
|
||||
= _('Add protected branch')
|
||||
.gl-new-card-description.gl-mt-2.gl-sm-mt-0
|
||||
= s_("ProtectedBranch|By default, protected branches restrict who can modify the branch.")
|
||||
= link_to s_("ProtectedBranch|Learn more."), help_page_path("user/project/protected_branches", anchor: "who-can-modify-a-protected-branch")
|
||||
- c.with_body do
|
||||
- if can_admin_entity
|
||||
.gl-new-card-add-form.gl-m-3.gl-display-none.js-toggle-content
|
||||
= content_for :create_protected_branch
|
||||
|
||||
= content_for :branches_list
|
||||
= content_for :branches_list
|
||||
|
||||
= paginate @protected_branches, theme: 'gitlab'
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
- protected_branch_test_type = protected_branch.project_level? ? 'project-level' : 'group-level'
|
||||
|
||||
%tr.js-protected-branch-edit-form{ data: { url: url, testid: 'protected-branch', test_type: protected_branch_test_type } }
|
||||
%td
|
||||
%span.ref-name= protected_branch.name
|
||||
%td{ class: 'gl-vertical-align-middle!', data: { label: s_("ProtectedBranch|Branch") } }
|
||||
%div
|
||||
%span.ref-name= protected_branch.name
|
||||
|
||||
- if protected_branch.project_level?
|
||||
- if protected_branch_entity.root_ref?(protected_branch.name)
|
||||
= gl_badge_tag s_('ProtectedBranch|default'), variant: :info
|
||||
- if protected_branch.project_level?
|
||||
- if protected_branch_entity.root_ref?(protected_branch.name)
|
||||
= gl_badge_tag s_('ProtectedBranch|default'), variant: :info
|
||||
|
||||
%div
|
||||
- if protected_branch.wildcard?
|
||||
- matching_branches = protected_branch.matching(repository.branch_names)
|
||||
= link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
|
||||
|
|
@ -22,9 +22,9 @@
|
|||
= render_if_exists 'protected_branches/ee/code_owner_approval_table', can_update: local_assigns[:can_update], protected_branch: protected_branch, protected_branch_entity: protected_branch_entity
|
||||
|
||||
- if can_admin_entity
|
||||
%td.text-right{ data: { testid: 'protected-branch-action' } }
|
||||
%td.text-right{ data: { label: _('Actions'), testid: 'protected-branch-action' } }
|
||||
- if local_assigns[:is_inherited]
|
||||
%span.has-tooltip{ data: { container: 'body' }, title: s_('ProtectedBranch|Inherited - This setting can be changed at the group level'), 'aria-hidden': 'true' }
|
||||
= sprite_icon 'lock'
|
||||
- else
|
||||
= link_button_to s_('ProtectedBranch|Unprotect'), [protected_branch_entity, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], aria: { label: s_('ProtectedBranch|Unprotect branch') }, data: { confirm: s_('ProtectedBranch|Branch will be writable for developers. Are you sure?'), confirm_btn_variant: 'danger' }, method: :delete, variant: :danger, size: :small
|
||||
= link_button_to s_('ProtectedBranch|Unprotect'), [protected_branch_entity, protected_branch, { update_section: 'js-protected-branches-settings' }], disabled: local_assigns[:disabled], aria: { label: s_('ProtectedBranch|Unprotect branch') }, data: { confirm: s_('ProtectedBranch|Branch will be writable for developers. Are you sure?'), confirm_btn_variant: 'danger' }, method: :delete, variant: :danger, category: :secondary, size: :small
|
||||
|
|
|
|||
|
|
@ -4,34 +4,18 @@ group: Respond
|
|||
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
|
||||
---
|
||||
|
||||
# Grafana Configuration **(FREE SELF)**
|
||||
# Configure Grafana **(FREE SELF)**
|
||||
|
||||
> - Grafana bundled with GitLab was [deprecated](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7772) in GitLab 16.0.
|
||||
> - Grafana bundled with GitLab was [removed](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7772) in GitLab 16.3.
|
||||
|
||||
WARNING:
|
||||
Grafana bundled with GitLab was deprecated GitLab 16.0 and removed in GitLab 16.3.
|
||||
For more information, see [deprecation notes](#deprecation-of-bundled-grafana).
|
||||
|
||||
[Grafana](https://grafana.com/) is a tool that enables you to visualize time
|
||||
series metrics through graphs and dashboards. GitLab writes performance data to Prometheus,
|
||||
and Grafana allows you to query the data to display graphs.
|
||||
|
||||
## Deprecation of bundled Grafana
|
||||
|
||||
Bundled Grafana was an optional service for Linux package installations that provided a user interface to GitLab metrics.
|
||||
|
||||
The version of Grafana that is bundled with Linux package installations is no longer supported. If you're using the
|
||||
bundled Grafana, you should switch to a newer version from [Grafana Labs](https://grafana.com/grafana/).
|
||||
|
||||
## Switch to new Grafana instance
|
||||
|
||||
To switch away from bundled Grafana to a newer version of Grafana from Grafana Labs:
|
||||
|
||||
1. Set up a version of Grafana from Grafana Labs.
|
||||
1. [Export the existing dashboards](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#export-a-dashboard) from bundled Grafana.
|
||||
1. [Import the existing dashboards](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#import-a-dashboard) in the new Grafana instance.
|
||||
1. [Configure GitLab](#integrate-with-gitlab-ui) to use the new Grafana instance.
|
||||
WARNING:
|
||||
Grafana bundled with GitLab was deprecated GitLab 16.0 and removed in GitLab 16.3.
|
||||
For more information, see [deprecation notes](#deprecation-of-bundled-grafana).
|
||||
|
||||
## Import GitLab dashboards
|
||||
|
||||
|
|
@ -80,3 +64,19 @@ configuration screen:
|
|||
|
||||
- No scopes appear.
|
||||
- The `read_user` scope is included.
|
||||
|
||||
## Deprecation of bundled Grafana
|
||||
|
||||
Bundled Grafana was an optional service for Linux package installations that provided a user interface to GitLab metrics.
|
||||
|
||||
The version of Grafana that is bundled with Linux package installations is no longer supported. If you're using the
|
||||
bundled Grafana, you should switch to a newer version from [Grafana Labs](https://grafana.com/grafana/).
|
||||
|
||||
### Switch to new Grafana instance
|
||||
|
||||
To switch away from bundled Grafana to a newer version of Grafana from Grafana Labs:
|
||||
|
||||
1. Set up a version of Grafana from Grafana Labs.
|
||||
1. [Export the existing dashboards](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#export-a-dashboard) from bundled Grafana.
|
||||
1. [Import the existing dashboards](https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/#import-a-dashboard) in the new Grafana instance.
|
||||
1. [Configure GitLab](#integrate-with-gitlab-ui) to use the new Grafana instance.
|
||||
|
|
|
|||
|
|
@ -7,18 +7,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# GitLab quick actions **(FREE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/26672) in GitLab 12.1:
|
||||
> once an action is executed, an alert appears when a quick action is successfully applied.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16877) in GitLab 13.2: you can use
|
||||
> quick actions when updating the description of issues, epics, and merge requests.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292393) in GitLab 13.8: when you enter
|
||||
> `/` into a description or comment field, all available quick actions are displayed in a scrollable list.
|
||||
> - The rebase quick action was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49800) in GitLab 13.8.
|
||||
|
||||
Quick actions are text-based shortcuts for common actions that are usually done
|
||||
by selecting buttons or dropdowns in the GitLab user interface. You can enter
|
||||
these commands in the descriptions or comments of issues, epics, merge requests,
|
||||
and commits.
|
||||
and commits. Quick actions are executed from both new comments and description, and when you edit
|
||||
existing ones.
|
||||
|
||||
Many quick actions are context-aware, requiring certain conditions be met. For example, to remove
|
||||
an issue due date with `/remove_due_date`, the issue must have a due date set.
|
||||
|
|
@ -49,7 +42,7 @@ The following quick actions are applicable to descriptions, discussions, and
|
|||
threads. Some quick actions might not be available to all subscription tiers.
|
||||
|
||||
<!-- Keep this table sorted alphabetically -->
|
||||
<!-- Don't prettify this table; it will expand out the last field into illegibility -->
|
||||
<!-- Don't auto-prettify this table; it will expand out the last field into illegibility -->
|
||||
|
||||
| Command | Issue | Merge request | Epic | Action |
|
||||
|:-------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
|
||||
|
|
@ -60,11 +53,13 @@ threads. Some quick actions might not be available to all subscription tiers.
|
|||
| `/assign_reviewer @user1 @user2` or `/reviewer @user1 @user2` or `/request_review @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users as reviewers.
|
||||
| `/assign_reviewer me` or `/reviewer me` or `/request_review me` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself as a reviewer.
|
||||
| `/award :emoji:` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Toggle an emoji reaction.
|
||||
| `/blocked_by <issue1> <issue2>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Mark the issue as blocked by other issues. The `<issue>` value should be in the format of `#issue`, `group/project#issue`, or the full issue URL. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214232) in GitLab 16.0).
|
||||
| `/blocks <issue1> <issue2>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Mark the issue as blocking other issues. The `<issue>` value should be in the format of `#issue`, `group/project#issue`, or the full issue URL. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214232) in GitLab 16.0).
|
||||
| `/cc @user` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mention a user. In GitLab 15.0 and later, this command performs no action. You can instead type `CC @user` or only `@user`. [In GitLab 14.9 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/31200), mentioning a user at the start of a line created a specific type of to-do item notification. |
|
||||
| `/child_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Add child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic.
|
||||
| `/clear_health_status` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear [health status](issues/managing_issues.md#health-status) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213814) in GitLab 14.7).
|
||||
| `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight.
|
||||
| `/clone <path/to/project> [--with_notes]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clone the issue to given project, or the current one if no arguments are given ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9421) in GitLab 13.7). Copies as much data as possible as long as the target project contains equivalent labels, milestones, and so on. Does not copy comments or system notes unless `--with_notes` is provided as an argument.
|
||||
| `/clone <path/to/project> [--with_notes]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clone the issue to given project, or the current one if no arguments are given. Copies as much data as possible as long as the target project contains equivalent objects like labels, milestones, or epics. Does not copy comments or system notes unless `--with_notes` is provided as an argument.
|
||||
| `/close` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Close.
|
||||
| `/confidential` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Mark issue or epic as confidential. Support for epics [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213741) in GitLab 15.6. |
|
||||
| `/copy_metadata <!merge_request>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another merge request in the project.
|
||||
|
|
@ -78,18 +73,18 @@ threads. Some quick actions might not be available to all subscription tiers.
|
|||
| `/estimate <time>` or `/estimate_time <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. For more information, see [Time tracking](time_tracking.md). Alias `/estimate_time` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6.
|
||||
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213814) in GitLab 14.7).
|
||||
| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates.
|
||||
| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196795) in GitLab 13.1).
|
||||
| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"`.
|
||||
| `/label ~label1 ~label2` or `/labels ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported.
|
||||
| `/lock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Lock the discussions.
|
||||
| `/link` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a link and description to [linked resources](../../operations/incident_management/linked_resources.md) in an incident ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/374964) in GitLab 15.5). |
|
||||
| `/lock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Lock the discussions.
|
||||
| `/merge` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Merge changes. Depending on the project setting, this may be [when the pipeline succeeds](merge_requests/merge_when_pipeline_succeeds.md), or adding to a [Merge Train](../../ci/pipelines/merge_trains.md).
|
||||
| `/milestone %milestone` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set milestone.
|
||||
| `/move <path/to/project>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Move this issue to another project. Be careful when moving an issue to a project with different access rules. Before moving the issue, make sure it does not contain sensitive data.
|
||||
| `/parent_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Set parent epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic.
|
||||
| `/promote` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to epic.
|
||||
| `/promote_to_incident` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to incident ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/296787) in GitLab 14.5). In [GitLab 15.8 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/376760), you can also use the quick action when creating a new issue.
|
||||
| `/page <policy name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Start escalations for the incident ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79977) in GitLab 14.9).
|
||||
| `/publish` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Publish issue to an associated [Status Page](../../operations/incident_management/status_page.md) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30906) in GitLab 13.0)
|
||||
| `/parent_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Set parent epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic.
|
||||
| `/promote_to_incident` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to incident ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/296787) in GitLab 14.5). In [GitLab 15.8 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/376760), you can also use the quick action when creating a new issue.
|
||||
| `/promote` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Promote issue to epic.
|
||||
| `/publish` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Publish issue to an associated [Status Page](../../operations/incident_management/status_page.md).
|
||||
| `/ready` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set the [ready status](merge_requests/drafts.md#mark-merge-requests-as-ready) ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90361) in GitLab 15.1).
|
||||
| `/reassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Replace current assignees with those specified.
|
||||
| `/reassign_reviewer @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Replace current reviewers with those specified.
|
||||
|
|
@ -101,37 +96,35 @@ threads. Some quick actions might not be available to all subscription tiers.
|
|||
| `/remove_due_date` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove due date.
|
||||
| `/remove_epic` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove from epic.
|
||||
| `/remove_estimate` or `/remove_time_estimate` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove time estimate. Alias `/remove_time_estimate` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6.
|
||||
| `/remove_iteration` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove iteration ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/196795) in GitLab 13.1).
|
||||
| `/remove_iteration` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove iteration.
|
||||
| `/remove_milestone` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove milestone.
|
||||
| `/remove_parent_epic` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Remove parent epic from epic.
|
||||
| `/remove_time_spent` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove time spent.
|
||||
| `/remove_zoom` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove Zoom meeting from this issue.
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen.
|
||||
| `/severity <severity>` | **{check-circle}** Yes | **{check-circle}** No | **{check-circle}** No | Set the severity. Issue type must be `Incident`. Options for `<severity>` are `S1` ... `S4`, `critical`, `high`, `medium`, `low`, `unknown`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334045) in GitLab 14.2.
|
||||
| `/severity <severity>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set the severity. Issue type must be `Incident`. Options for `<severity>` are `S1` ... `S4`, `critical`, `high`, `medium`, `low`, `unknown`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334045) in GitLab 14.2.
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`.
|
||||
| `/spend <time> [<date>]` or `/spend_time <time> [<date>]` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Add or subtract spent time. Optionally, specify the date that time was spent on. For example, `/spend 1mo 2w 3d 4h 5m 2018-08-26` or `/spend -1h 30m`. For more information, see [Time tracking](time_tracking.md). Alias `/spend_time` [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16501) in GitLab 15.6.
|
||||
| `/submit_review` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Submit a pending review.
|
||||
| `/subscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Subscribe to notifications.
|
||||
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`.
|
||||
| `/target_branch <local branch name>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set target branch.
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title.
|
||||
| `/timeline <timeline comment> \| <date(YYYY-MM-DD)> <time(HH:MM)>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a timeline event to this incident. For example, `/timeline DB load spiked \| 2022-09-07 09:30`. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368721) in GitLab 15.4). |
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title.
|
||||
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item.
|
||||
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8103) in GitLab 14.3.
|
||||
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8103) in GitLab 14.3).
|
||||
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific assignees.
|
||||
| `/unassign` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all assignees.
|
||||
| `/unassign_reviewer @user1 @user2` or `/remove_reviewer @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific reviewers.
|
||||
| `/unassign_reviewer me` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove yourself as a reviewer.
|
||||
| `/unassign_reviewer` or `/remove_reviewer` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all reviewers.
|
||||
| `/unassign` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all assignees.
|
||||
| `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove specified labels.
|
||||
| `/unlabel` or `/remove_label` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove all labels.
|
||||
| `/unlink <issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove link with to the provided issue. The `<issue>` value should be in the format of `#issue`, `group/project#issue`, or the full issue URL. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/414400) in GitLab 16.1).
|
||||
| `/unlock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Unlock the discussions.
|
||||
| `/unsubscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Unsubscribe from notifications.
|
||||
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, `2`, and so on.
|
||||
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid values are integers like `0`, `1`, or `2`.
|
||||
| `/zoom <Zoom URL>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a Zoom meeting to this issue or incident. In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/230853) users on GitLab Premium can add a short description when [adding a Zoom link to an incident](../../operations/incident_management/linked_resources.md#link-zoom-meetings-from-an-incident).
|
||||
| `/blocks <issue1> <issue2>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Mark the issue as blocking other issues. The `<issue>` value should be in the format of `#issue`, `group/project#issue`, or the full issue URL. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214232) in GitLab 16.0).
|
||||
| `/blocked_by <issue1> <issue2>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Mark the issue as blocked by other issues. The `<issue>` value should be in the format of `#issue`, `group/project#issue`, or the full issue URL. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214232) in GitLab 16.0).
|
||||
| `/unlink <issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove link with to the provided issue. The `<issue>` value should be in the format of `#issue`, `group/project#issue`, or the full issue URL. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/414400) in GitLab 16.1).
|
||||
|
||||
## Work items
|
||||
|
||||
|
|
@ -140,33 +133,36 @@ threads. Some quick actions might not be available to all subscription tiers.
|
|||
Work items in GitLab include [tasks](../tasks.md) and [OKRs](../okrs.md).
|
||||
The following quick actions can be applied through the description field when editing or commenting on work items.
|
||||
|
||||
| Command | Task | Objective | Key Result | Action
|
||||
|:-------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title.
|
||||
| `/close` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Close.
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen.
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`.
|
||||
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`.
|
||||
| `/cc @user` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mention a user. In GitLab 15.0 and later, this command performs no action. You can instead type `CC @user` or only `@user`. [In GitLab 14.9 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/31200), mentioning a user at the start of a line creates a specific type of to-do item notification.
|
||||
| `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** Yes | Assign one or more users.
|
||||
| `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** Yes | Assign yourself.
|
||||
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** Yes | Remove specific assignees.
|
||||
| `/unassign` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** Yes | Remove all assignees.
|
||||
| `/reassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** Yes | Replace current assignees with those specified.
|
||||
| `/label ~label1 ~label2` or `/labels ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported.
|
||||
| `/relabel ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Replace current labels with those specified.
|
||||
| `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove specified labels.
|
||||
| `/unlabel` or `/remove_label` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove all labels.
|
||||
| `/due <date>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** Yes | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`.
|
||||
| `/remove_due_date` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** Yes | Remove due date.
|
||||
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** Yes | **{dotted-circle}** Yes | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk`.
|
||||
| `/clear_health_status` | **{check-circle}** Yes | **{dotted-circle}** Yes | **{dotted-circle}** Yes | Clear [health status](issues/managing_issues.md#health-status).
|
||||
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, and `2`.
|
||||
| `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight.
|
||||
| `/type` | **{check-circle}** Yes | **{dotted-circle}** Yes | **{dotted-circle}** Yes | Converts work item to specified type. Available options for `<type>` include `issue`, `task`, `objective` and `key result`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385227) in GitLab 16.0.
|
||||
| `/promote_to <type>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Promotes work item to specified type. Available options for `<type>`: `issue` (promote a task) and `objective` (promote a key result). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412534) in GitLab 16.1.
|
||||
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412277) in GitLab 16.2.
|
||||
| `/done` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mark to-do item as done. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412277) in GitLab 16.2.
|
||||
<!-- Keep this table sorted alphabetically -->
|
||||
<!-- Don't auto-prettify this table; it will expand out the last field into illegibility -->
|
||||
|
||||
| Command | Task | Objective | Key Result | Action
|
||||
|:--------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
|
||||
| `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Assign one or more users.
|
||||
| `/assign me` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Assign yourself.
|
||||
| `/cc @user` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mention a user. In GitLab 15.0 and later, this command performs no action. You can instead type `CC @user` or only `@user`. [In GitLab 14.9 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/31200), mentioning a user at the start of a line creates a specific type of to-do item notification.
|
||||
| `/clear_health_status` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Clear [health status](issues/managing_issues.md#health-status).
|
||||
| `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight.
|
||||
| `/close` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Close.
|
||||
| `/done` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mark to-do item as done. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412277) in GitLab 16.2.
|
||||
| `/due <date>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`.
|
||||
| `/health_status <value>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, or `at_risk`.
|
||||
| `/label ~label1 ~label2` or `/labels ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported.
|
||||
| `/promote_to <type>` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Promotes work item to specified type. Available options for `<type>`: `issue` (promote a task) or `objective` (promote a key result). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412534) in GitLab 16.1.
|
||||
| `/reassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Replace current assignees with those specified.
|
||||
| `/relabel ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Replace current labels with those specified.
|
||||
| `/remove_due_date` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Remove due date.
|
||||
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen.
|
||||
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`.
|
||||
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ ┻━┻`.
|
||||
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title.
|
||||
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/412277) in GitLab 16.2.
|
||||
| `/type` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Converts work item to specified type. Available options for `<type>` include `issue`, `task`, `objective` and `key result`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/385227) in GitLab 16.0.
|
||||
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove specific assignees.
|
||||
| `/unassign` | **{dotted-circle}** No | **{check-circle}** Yes | **{check-circle}** Yes | Remove all assignees.
|
||||
| `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove specified labels.
|
||||
| `/unlabel` or `/remove_label` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Remove all labels.
|
||||
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, and `2`.
|
||||
|
||||
## Commit messages
|
||||
|
||||
|
|
@ -176,14 +172,15 @@ The following quick actions are applicable for commit messages:
|
|||
|:----------------------- |:------------------------------------------|
|
||||
| `/tag v1.2.3 <message>` | Tags the commit with an optional message. |
|
||||
|
||||
<!-- ## Troubleshooting
|
||||
## Troubleshooting
|
||||
|
||||
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
|
||||
important to describe those, too. Think of things that may go wrong and include them here.
|
||||
This is important to minimize requests for support, and to avoid doc comments with
|
||||
questions that you know someone might ask.
|
||||
### Quick action isn't executed
|
||||
|
||||
Each scenario can be a third-level heading, for example `### Getting error message X`.
|
||||
If you have none to add when creating a doc, leave this section in place
|
||||
but commented out to help encourage others to add to it in the future. -->
|
||||
If you run a quick action, but nothing happens, check if the quick action appears in the autocomplete
|
||||
box as you type it.
|
||||
If it doesn't, it's possible that:
|
||||
|
||||
- The feature related to the quick action isn't available to you based on your subscription tier or
|
||||
user role in the group or project.
|
||||
- A required condition for the quick action isn't met.
|
||||
For example, you're running `/unlabel` on an issue without any labels.
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ module Gitlab
|
|||
time_change
|
||||
severity
|
||||
escalation_status
|
||||
customer_relations_contacts
|
||||
].freeze
|
||||
end
|
||||
|
||||
|
|
@ -56,7 +57,8 @@ module Gitlab
|
|||
assignee_id: issue.assignee_ids.first, # This key is deprecated
|
||||
labels: issue.labels_hook_attrs,
|
||||
state: issue.state,
|
||||
severity: issue.severity
|
||||
severity: issue.severity,
|
||||
customer_relations_contacts: issue.customer_relations_contacts.map(&:hook_attrs)
|
||||
}
|
||||
|
||||
if issue.supports_escalation? && issue.escalation_status
|
||||
|
|
|
|||
|
|
@ -1777,7 +1777,7 @@ msgstr ""
|
|||
msgid "A complete DevOps platform"
|
||||
msgstr ""
|
||||
|
||||
msgid "A confidential issue cannot have a parent that already has non-confidential children."
|
||||
msgid "A confidential issue must have only confidential children. Make any child items confidential and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "A confidential work item cannot have a parent that already has non-confidential children."
|
||||
|
|
@ -2941,6 +2941,9 @@ msgstr ""
|
|||
msgid "Add projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add protected branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add reaction"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -37944,9 +37947,6 @@ msgstr ""
|
|||
msgid "ProtectedBranch|Protect a branch"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedBranch|Protected branch (%{protected_branches_count})"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedBranch|Protected branches"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -37965,7 +37965,7 @@ msgstr ""
|
|||
msgid "ProtectedBranch|Tag"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedBranch|There are currently no protected branches, protect a branch with the form above."
|
||||
msgid "ProtectedBranch|There are currently no protected branches, to protect a branch start by creating a new one above."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProtectedBranch|Toggle allowed to force push"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
diff --git a/node_modules/@vue/test-utils-vue3/dist/vue-test-utils.cjs.js b/node_modules/@vue/test-utils-vue3/dist/vue-test-utils.cjs.js
|
||||
index 2902a1e..4f5ab30 100644
|
||||
index 2902a1e..0c1e6b5 100644
|
||||
--- a/node_modules/@vue/test-utils-vue3/dist/vue-test-utils.cjs.js
|
||||
+++ b/node_modules/@vue/test-utils-vue3/dist/vue-test-utils.cjs.js
|
||||
@@ -7945,7 +7945,8 @@ function createStubComponentsTransformer(_a) {
|
||||
@@ -7945,7 +7945,9 @@ function createStubComponentsTransformer(_a) {
|
||||
var stubName = name || registeredName || componentName;
|
||||
return ((_c = (_b = (_a = config.plugins).createStubs) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
||||
name: stubName,
|
||||
- component: type
|
||||
+ component: type,
|
||||
+ registerStub
|
||||
+ registerStub,
|
||||
+ stubs
|
||||
})) !== null && _c !== void 0 ? _c : createStub({
|
||||
name: stubName,
|
||||
type: type,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ module QA
|
|||
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
|
||||
element :mirror_repository_url_field
|
||||
element :mirror_repository_button
|
||||
element 'add-new-mirror'
|
||||
end
|
||||
|
||||
view 'app/views/projects/mirrors/_mirror_repos_list.html.haml' do
|
||||
|
|
@ -37,6 +38,7 @@ module QA
|
|||
end
|
||||
|
||||
def repository_url=(value)
|
||||
click_element 'add-new-mirror'
|
||||
fill_element :mirror_repository_url_field, value
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
|
||||
it 'allows to create a protected branch with name containing HTML tags' do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_defaults
|
||||
set_protected_branch_name('foo<b>bar<\b>')
|
||||
click_on "Protect"
|
||||
|
|
@ -89,6 +91,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
describe "explicit protected branches" do
|
||||
it "allows creating explicit protected branches" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_defaults
|
||||
set_protected_branch_name('some->branch')
|
||||
click_on "Protect"
|
||||
|
|
@ -100,6 +104,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
|
||||
it "shows success alert once protected branch is created" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_defaults
|
||||
set_protected_branch_name('some->branch')
|
||||
click_on "Protect"
|
||||
|
|
@ -112,6 +118,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
project.repository.add_branch(admin, 'some-branch', commit.id)
|
||||
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_defaults
|
||||
set_protected_branch_name('some-branch')
|
||||
click_on "Protect"
|
||||
|
|
@ -124,6 +132,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
|
||||
it "displays an error message if the named branch does not exist" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_defaults
|
||||
set_protected_branch_name('some-branch')
|
||||
click_on "Protect"
|
||||
|
|
@ -135,6 +145,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
describe "wildcard protected branches" do
|
||||
it "allows creating protected branches with a wildcard" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_defaults
|
||||
set_protected_branch_name('*-stable')
|
||||
click_on "Protect"
|
||||
|
|
@ -149,6 +161,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
project.repository.add_branch(admin, 'staging-stable', 'master')
|
||||
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_defaults
|
||||
set_protected_branch_name('*-stable')
|
||||
click_on "Protect"
|
||||
|
|
@ -164,6 +178,8 @@ RSpec.describe 'Protected Branches', :js, feature_category: :source_code_managem
|
|||
project.repository.add_branch(admin, 'development', 'master')
|
||||
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_protected_branch_name('*-stable')
|
||||
set_defaults
|
||||
click_on "Protect"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import GlobalSearchDefaultIssuables from '~/super_sidebar/components/global_search/components/global_search_default_issuables.vue';
|
||||
import {
|
||||
MOCK_SEARCH_CONTEXT,
|
||||
MOCK_PROJECT_SEARCH_CONTEXT,
|
||||
MOCK_GROUP_SEARCH_CONTEXT,
|
||||
MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||
} from '../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
describe('GlobalSearchDefaultPlaces', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({
|
||||
searchContext = null,
|
||||
mockDefaultSearchOptions = [],
|
||||
...options
|
||||
} = {}) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
searchContext,
|
||||
},
|
||||
getters: {
|
||||
defaultSearchOptions: () => mockDefaultSearchOptions,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper = shallowMount(GlobalSearchDefaultIssuables, {
|
||||
store,
|
||||
stubs: {
|
||||
GlDisclosureDropdownGroup,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
const findGroup = () => wrapper.findComponent(GlDisclosureDropdownGroup);
|
||||
const findItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
|
||||
|
||||
describe('given no contextSwitcherLinks', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders nothing', () => {
|
||||
expect(wrapper.html()).toBe('');
|
||||
});
|
||||
|
||||
it('emits a nothing-to-render event', () => {
|
||||
expect(wrapper.emitted('nothing-to-render')).toEqual([[]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given some contextSwitcherLinks', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
searchContext: MOCK_SEARCH_CONTEXT,
|
||||
mockDefaultSearchOptions: MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||
attrs: {
|
||||
bordered: true,
|
||||
class: 'test-class',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders a disclosure dropdown group', () => {
|
||||
expect(findGroup().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the expected header', () => {
|
||||
expect(wrapper.text()).toContain('All GitLab');
|
||||
});
|
||||
|
||||
it('passes attrs down', () => {
|
||||
const group = findGroup();
|
||||
expect(group.props('bordered')).toBe(true);
|
||||
expect(group.classes()).toContain('test-class');
|
||||
});
|
||||
|
||||
it('renders the links', () => {
|
||||
const itemProps = findItems().wrappers.map((item) => item.props('item'));
|
||||
|
||||
expect(itemProps).toEqual([
|
||||
{
|
||||
text: 'Issues assigned to me',
|
||||
href: '/dashboard/issues/?assignee_username=anyone',
|
||||
},
|
||||
{
|
||||
text: "Issues I've created",
|
||||
href: '/dashboard/issues/?author_username=anyone',
|
||||
},
|
||||
{
|
||||
text: 'Merge requests assigned to me',
|
||||
href: '/dashboard/merge_requests/?assignee_username=anyone',
|
||||
},
|
||||
{
|
||||
text: "Merge requests that I'm a reviewer",
|
||||
href: '/dashboard/merge_requests/?reviewer_username=anyone',
|
||||
},
|
||||
{
|
||||
text: "Merge requests I've created",
|
||||
href: '/dashboard/merge_requests/?author_username=anyone',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('group name', () => {
|
||||
describe('in a project context', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
searchContext: MOCK_PROJECT_SEARCH_CONTEXT,
|
||||
mockDefaultSearchOptions: MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the expected header', () => {
|
||||
expect(wrapper.text()).toContain('MockProject');
|
||||
});
|
||||
});
|
||||
|
||||
describe('in a group context', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
searchContext: MOCK_GROUP_SEARCH_CONTEXT,
|
||||
mockDefaultSearchOptions: MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the expected header', () => {
|
||||
expect(wrapper.text()).toContain('MockGroup');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,110 +1,59 @@
|
|||
import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem } from '@gitlab/ui';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import GlobalSearchDefaultItems from '~/super_sidebar/components/global_search/components/global_search_default_items.vue';
|
||||
import { MOCK_SEARCH_CONTEXT, MOCK_DEFAULT_SEARCH_OPTIONS } from '../mock_data';
|
||||
import { contextSwitcherLinks } from '../../../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
import GlobalSearchDefaultPlaces from '~/super_sidebar/components/global_search/components/global_search_default_places.vue';
|
||||
import GlobalSearchDefaultIssuables from '~/super_sidebar/components/global_search/components/global_search_default_issuables.vue';
|
||||
|
||||
describe('GlobalSearchDefaultItems', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({
|
||||
storeState,
|
||||
mockDefaultSearchOptions = MOCK_DEFAULT_SEARCH_OPTIONS,
|
||||
...options
|
||||
} = {}) => {
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
searchContext: MOCK_SEARCH_CONTEXT,
|
||||
...storeState,
|
||||
},
|
||||
getters: {
|
||||
defaultSearchOptions: () => mockDefaultSearchOptions,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper = shallowMountExtended(GlobalSearchDefaultItems, {
|
||||
store,
|
||||
provide: {
|
||||
contextSwitcherLinks,
|
||||
},
|
||||
stubs: {
|
||||
GlDisclosureDropdownGroup,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(GlobalSearchDefaultItems);
|
||||
};
|
||||
|
||||
const findGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup);
|
||||
const findItems = (root = wrapper) => root.findAllComponents(GlDisclosureDropdownItem);
|
||||
const findPlaces = () => wrapper.findComponent(GlobalSearchDefaultPlaces);
|
||||
const findIssuables = () => wrapper.findComponent(GlobalSearchDefaultIssuables);
|
||||
const receivedAttrs = (wrapperInstance) => ({
|
||||
// See https://github.com/vuejs/test-utils/issues/2151.
|
||||
...wrapperInstance.vm.$attrs,
|
||||
});
|
||||
|
||||
describe('template', () => {
|
||||
describe('Dropdown items', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders two groups', () => {
|
||||
const groups = findGroups();
|
||||
|
||||
expect(groups).toHaveLength(2);
|
||||
|
||||
const actualNames = groups.wrappers.map((group) => group.props('group').name);
|
||||
expect(actualNames).toEqual(['Places', 'All GitLab']);
|
||||
});
|
||||
|
||||
it('renders context switcher links in first group', () => {
|
||||
const group = findGroups().at(0);
|
||||
expect(group.props('group').name).toBe('Places');
|
||||
|
||||
const items = findItems(group);
|
||||
expect(items).toHaveLength(contextSwitcherLinks.length);
|
||||
});
|
||||
|
||||
it('renders default search options in second group', () => {
|
||||
const group = findGroups().at(1);
|
||||
expect(group.props('group').name).toBe('All GitLab');
|
||||
|
||||
const items = findItems(group);
|
||||
expect(items).toHaveLength(MOCK_DEFAULT_SEARCH_OPTIONS.length);
|
||||
});
|
||||
describe('all child components can render', () => {
|
||||
it('renders the components', () => {
|
||||
expect(findPlaces().exists()).toBe(true);
|
||||
expect(findIssuables().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('Empty groups', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ mockDefaultSearchOptions: [], provide: { contextSwitcherLinks: [] } });
|
||||
});
|
||||
|
||||
it('does not render groups with no items', () => {
|
||||
expect(findGroups()).toHaveLength(0);
|
||||
});
|
||||
it('sets the expected props on first component', () => {
|
||||
const places = findPlaces();
|
||||
expect(receivedAttrs(places)).toEqual({});
|
||||
expect(places.classes()).toEqual([]);
|
||||
});
|
||||
|
||||
describe.each`
|
||||
group | project | groupHeader
|
||||
${null} | ${null} | ${'All GitLab'}
|
||||
${{ name: 'Test Group' }} | ${null} | ${'Test Group'}
|
||||
${{ name: 'Test Group' }} | ${{ name: 'Test Project' }} | ${'Test Project'}
|
||||
`('Current context header', ({ group, project, groupHeader }) => {
|
||||
describe(`when group is ${group?.name} and project is ${project?.name}`, () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
storeState: {
|
||||
searchContext: {
|
||||
group,
|
||||
project,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
it('sets the expected props on second component', () => {
|
||||
const issuables = findIssuables();
|
||||
expect(receivedAttrs(issuables)).toEqual({ bordered: true });
|
||||
expect(issuables.classes()).toEqual(['gl-mt-3']);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should render as ${groupHeader}`, () => {
|
||||
expect(wrapper.text()).toContain(groupHeader);
|
||||
});
|
||||
});
|
||||
describe('when a child component emits nothing-to-render', () => {
|
||||
beforeEach(() => {
|
||||
findPlaces().vm.$emit('nothing-to-render');
|
||||
});
|
||||
|
||||
it('does not render the component', () => {
|
||||
expect(findPlaces().exists()).toBe(false);
|
||||
expect(findIssuables().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('sets the expected props on first component', () => {
|
||||
const issuables = findIssuables();
|
||||
expect(receivedAttrs(issuables)).toEqual({});
|
||||
expect(issuables.classes()).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import { GlDisclosureDropdownGroup, GlDisclosureDropdownItem } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import GlobalSearchDefaultPlaces from '~/super_sidebar/components/global_search/components/global_search_default_places.vue';
|
||||
import { contextSwitcherLinks } from '../../../mock_data';
|
||||
|
||||
describe('GlobalSearchDefaultPlaces', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({ links = [], attrs } = {}) => {
|
||||
wrapper = shallowMount(GlobalSearchDefaultPlaces, {
|
||||
provide: {
|
||||
contextSwitcherLinks: links,
|
||||
},
|
||||
attrs,
|
||||
stubs: {
|
||||
GlDisclosureDropdownGroup,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findGroup = () => wrapper.findComponent(GlDisclosureDropdownGroup);
|
||||
const findItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
|
||||
|
||||
describe('given no contextSwitcherLinks', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('renders nothing', () => {
|
||||
expect(wrapper.html()).toBe('');
|
||||
});
|
||||
|
||||
it('emits a nothing-to-render event', () => {
|
||||
expect(wrapper.emitted('nothing-to-render')).toEqual([[]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('given some contextSwitcherLinks', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
links: contextSwitcherLinks,
|
||||
attrs: {
|
||||
bordered: true,
|
||||
class: 'test-class',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders a disclosure dropdown group', () => {
|
||||
expect(findGroup().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the expected header', () => {
|
||||
expect(wrapper.text()).toContain('Places');
|
||||
});
|
||||
|
||||
it('passes attrs down', () => {
|
||||
const group = findGroup();
|
||||
expect(group.props('bordered')).toBe(true);
|
||||
expect(group.classes()).toContain('test-class');
|
||||
});
|
||||
|
||||
it('renders the links', () => {
|
||||
const itemProps = findItems().wrappers.map((item) => item.props('item'));
|
||||
|
||||
expect(itemProps).toEqual([
|
||||
{
|
||||
text: 'Explore',
|
||||
href: '/explore',
|
||||
},
|
||||
{
|
||||
text: 'Admin area',
|
||||
href: '/admin',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -73,6 +73,7 @@ export const mergeRequestMenuGroup = [
|
|||
|
||||
export const contextSwitcherLinks = [
|
||||
{ title: 'Explore', link: '/explore', icon: 'compass', link_classes: 'persistent-link-class' },
|
||||
{ title: 'Admin area', link: '/admin', icon: 'admin' },
|
||||
];
|
||||
|
||||
export const sidebarData = {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@ function isLegacyExtendedComponent(component) {
|
|||
function unwrapLegacyVueExtendComponent(selector) {
|
||||
return isLegacyExtendedComponent(selector) ? selector.options : selector;
|
||||
}
|
||||
function getStubProps(component) {
|
||||
const stubProps = { ...component.props };
|
||||
component.mixins?.forEach((mixin) => {
|
||||
Object.assign(stubProps, unwrapLegacyVueExtendComponent(mixin).props);
|
||||
});
|
||||
return stubProps;
|
||||
}
|
||||
|
||||
if (global.document) {
|
||||
const compatConfig = {
|
||||
|
|
@ -148,33 +155,27 @@ if (global.document) {
|
|||
return true;
|
||||
};
|
||||
|
||||
VTU.config.plugins.createStubs = ({ name, component: rawComponent, registerStub }) => {
|
||||
VTU.config.plugins.createStubs = ({ name, component: rawComponent, registerStub, stubs }) => {
|
||||
const component = unwrapLegacyVueExtendComponent(rawComponent);
|
||||
const hyphenatedName = name.replace(/\B([A-Z])/g, '-$1').toLowerCase();
|
||||
const stubTag = stubs?.[name] ? name : hyphenatedName;
|
||||
|
||||
const stub = Vue.defineComponent({
|
||||
name: getComponentName(component),
|
||||
props: component.props,
|
||||
model: component.model,
|
||||
props: getStubProps(component),
|
||||
model: component.model ?? component.mixins?.find((m) => m.model),
|
||||
methods: Object.fromEntries(
|
||||
Object.entries(component.methods ?? {}).map(([key]) => [key, noop]),
|
||||
),
|
||||
render() {
|
||||
const {
|
||||
$slots: slots = {},
|
||||
$scopedSlots: scopedSlots = {},
|
||||
$parent: parent,
|
||||
$vnode: vnode,
|
||||
} = this;
|
||||
const { $scopedSlots: scopedSlots = {} } = this;
|
||||
|
||||
const hasStaticDefaultSlot = 'default' in slots && !('default' in scopedSlots);
|
||||
const isTheOnlyChild = parent?.$.subTree === vnode;
|
||||
// this condition should be altered when https://github.com/vuejs/vue-test-utils/pull/2068 is merged
|
||||
// and our codebase will be updated to include it (@vue/test-utils@1.3.6 I assume)
|
||||
const shouldRenderAllSlots = !hasStaticDefaultSlot && isTheOnlyChild;
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const hasDefaultSlot = 'default' in scopedSlots && scopedSlots.default._ns;
|
||||
const shouldRenderAllSlots = !component.functional && !hasDefaultSlot;
|
||||
|
||||
const renderSlotByName = (slotName) => {
|
||||
const slot = scopedSlots[slotName] || slots[slotName];
|
||||
const slot = scopedSlots[slotName];
|
||||
let result;
|
||||
if (typeof slot === 'function') {
|
||||
try {
|
||||
|
|
@ -189,16 +190,16 @@ if (global.document) {
|
|||
};
|
||||
|
||||
const slotContents = shouldRenderAllSlots
|
||||
? [...new Set([...Object.keys(slots), ...Object.keys(scopedSlots)])]
|
||||
.map(renderSlotByName)
|
||||
.filter(Boolean)
|
||||
? Object.keys(scopedSlots).map(renderSlotByName).filter(Boolean)
|
||||
: renderSlotByName('default');
|
||||
|
||||
const props = Object.fromEntries(
|
||||
Object.entries(this.$props).filter(([prop]) => isPropertyValidOnDomNode(prop)),
|
||||
Object.entries(this.$props)
|
||||
.filter(([prop]) => isPropertyValidOnDomNode(prop))
|
||||
.map(([key, value]) => [key, typeof value === 'function' ? '[Function]' : value]),
|
||||
);
|
||||
|
||||
return Vue.h(`${hyphenatedName || 'anonymous'}-stub`, props, slotContents);
|
||||
return Vue.h(`${stubTag || 'anonymous'}-stub`, props, slotContents);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::HookData::IssueBuilder do
|
||||
let_it_be(:label) { create(:label) }
|
||||
let_it_be(:issue) { create(:labeled_issue, labels: [label], project: label.project) }
|
||||
RSpec.describe Gitlab::HookData::IssueBuilder, feature_category: :webhooks do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, group: group) }
|
||||
let_it_be(:label) { create(:label, project: project) }
|
||||
let_it_be(:issue) { create(:labeled_issue, labels: [label], project: project) }
|
||||
let_it_be(:contact) { create(:contact, group: project.group) }
|
||||
let_it_be(:issue_contact) { create(:issue_customer_relations_contact, issue: issue, contact: contact) }
|
||||
|
||||
let(:builder) { described_class.new(issue) }
|
||||
|
||||
|
|
@ -50,6 +54,7 @@ RSpec.describe Gitlab::HookData::IssueBuilder do
|
|||
expect(data).to include(:state)
|
||||
expect(data).to include(:severity)
|
||||
expect(data).to include('labels' => [label.hook_attrs])
|
||||
expect(data).to include('customer_relations_contacts' => [contact.reload.hook_attrs])
|
||||
end
|
||||
|
||||
context 'when the issue has an image in the description' do
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe CustomerRelations::Contact, type: :model do
|
||||
RSpec.describe CustomerRelations::Contact, type: :model, feature_category: :team_planning do
|
||||
let_it_be(:group) { create(:group) }
|
||||
|
||||
describe 'associations' do
|
||||
|
|
@ -280,4 +280,25 @@ RSpec.describe CustomerRelations::Contact, type: :model do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hook_attrs' do
|
||||
let_it_be(:contact) { create(:contact, group: group) }
|
||||
|
||||
it 'includes the expected attributes' do
|
||||
expect(contact.hook_attrs).to match a_hash_including(
|
||||
{
|
||||
'created_at' => contact.created_at,
|
||||
'description' => contact.description,
|
||||
'first_name' => contact.first_name,
|
||||
'group_id' => group.id,
|
||||
'id' => contact.id,
|
||||
'last_name' => contact.last_name,
|
||||
'organization_id' => contact.organization_id,
|
||||
'state' => contact.state,
|
||||
'updated_at' => contact.updated_at
|
||||
}
|
||||
)
|
||||
expect(contact.hook_attrs.keys).to match_array(described_class::SAFE_ATTRIBUTES)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ RSpec.describe Issue, feature_category: :team_planning do
|
|||
|
||||
expect(issue).not_to be_valid
|
||||
expect(issue.errors[:base])
|
||||
.to include(_('A confidential issue cannot have a parent that already has non-confidential children.'))
|
||||
.to include(_('A confidential issue must have only confidential children. Make any child items confidential and try again.'))
|
||||
end
|
||||
|
||||
it 'allows to make child confidential' do
|
||||
|
|
|
|||
|
|
@ -3266,6 +3266,15 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with check_mergeability_retry_lease option' do
|
||||
it 'call check_mergeability with sync_retry_lease' do
|
||||
allow(subject).to receive(:mergeable_state?) { true }
|
||||
expect(subject).to receive(:check_mergeability).with(sync_retry_lease: true)
|
||||
|
||||
subject.mergeable?(check_mergeability_retry_lease: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#skipped_mergeable_checks' do
|
||||
|
|
@ -3300,6 +3309,14 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
|
|||
subject.check_mergeability
|
||||
end
|
||||
|
||||
context 'when sync_retry_lease is true' do
|
||||
it 'executes MergeabilityCheckService' do
|
||||
expect(mergeability_service).to receive(:execute).with(retry_lease: true)
|
||||
|
||||
subject.check_mergeability(sync_retry_lease: true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when async is true' do
|
||||
it 'executes MergeabilityCheckService asynchronously' do
|
||||
expect(mergeability_service).to receive(:async_execute)
|
||||
|
|
|
|||
|
|
@ -577,6 +577,14 @@ RSpec.describe MergeRequests::MergeService, feature_category: :code_review_workf
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when passing `check_mergeability_retry_lease: true` as `options` parameter' do
|
||||
it 'call mergeable? with check_mergeability_retry_lease' do
|
||||
expect(merge_request).to receive(:mergeable?).with(hash_including(check_mergeability_retry_lease: true)).and_call_original
|
||||
|
||||
service.execute(merge_request, check_mergeability_retry_lease: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ RSpec.configure do |config|
|
|||
config.include ::Ci::PartitioningTesting::PartitionIdentifiers
|
||||
|
||||
config.around(:each, :ci_partitionable) do |example|
|
||||
unless ::Ci::Build.table_name.to_s.starts_with?('p_')
|
||||
skip 'Skipping partitioning tests until `ci_builds` is partitioned'
|
||||
end
|
||||
|
||||
::Ci::PartitioningTesting::SchemaHelpers.with_routing_tables do
|
||||
example.run
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,16 +6,22 @@ module Ci
|
|||
module_function
|
||||
|
||||
def with_routing_tables
|
||||
# model.table_name = :routing_table
|
||||
previous_table_name = CommitStatus.table_name
|
||||
CommitStatus.table_name = :p_ci_builds
|
||||
CommitStatus.descendants.each(&:reset_table_name)
|
||||
|
||||
yield
|
||||
# ensure
|
||||
# model.table_name = :regular_table
|
||||
|
||||
ensure
|
||||
CommitStatus.table_name = previous_table_name
|
||||
CommitStatus.descendants.each(&:reset_table_name)
|
||||
end
|
||||
|
||||
def setup(connection: Ci::ApplicationRecord.connection)
|
||||
each_partitionable_table do |table_name|
|
||||
create_test_partition("p_#{table_name}", connection: connection)
|
||||
end
|
||||
ensure_builds_id_uniquness(connection: connection)
|
||||
end
|
||||
|
||||
def teardown(connection: Ci::ApplicationRecord.connection)
|
||||
|
|
@ -57,6 +63,16 @@ module Ci
|
|||
SQL
|
||||
end
|
||||
|
||||
# This can be removed after https://gitlab.com/gitlab-org/gitlab/-/issues/421173
|
||||
# is implemented
|
||||
def ensure_builds_id_uniquness(connection:)
|
||||
connection.execute(<<~SQL.squish)
|
||||
CREATE TRIGGER assign_p_ci_builds_id_trigger
|
||||
BEFORE INSERT ON #{full_partition_name('ci_builds')}
|
||||
FOR EACH ROW EXECUTE FUNCTION assign_p_ci_builds_id_value();
|
||||
SQL
|
||||
end
|
||||
|
||||
def table_available?(table_name, connection:)
|
||||
connection.table_exists?(table_name) &&
|
||||
connection.column_exists?(table_name, :partition_id)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ module ProtectedBranchHelpers
|
|||
end
|
||||
end
|
||||
|
||||
def show_add_form
|
||||
click_button 'Add protected branch'
|
||||
end
|
||||
|
||||
def set_protected_branch_name(branch_name)
|
||||
find('.js-protected-branch-select').click
|
||||
find('.dropdown-input-field').set(branch_name)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
|
|||
it "allows creating protected branches that #{access_type_name} can push to" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_protected_branch_name('master')
|
||||
set_allowed_to('merge', no_one)
|
||||
set_allowed_to('push', access_type_name)
|
||||
|
|
@ -19,6 +20,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
|
|||
it "allows creating protected branches that #{access_type_name} can merge to" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_protected_branch_name('master')
|
||||
set_allowed_to('merge', access_type_name)
|
||||
set_allowed_to('push', no_one)
|
||||
|
|
@ -31,6 +33,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
|
|||
it "allows updating protected branches so that #{access_type_name} can push to them" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_protected_branch_name('master')
|
||||
set_allowed_to('merge', no_one)
|
||||
set_allowed_to('push', no_one)
|
||||
|
|
@ -52,6 +55,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
|
|||
it "allows updating protected branches so that #{access_type_name} can merge to them" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
show_add_form
|
||||
set_protected_branch_name('master')
|
||||
set_allowed_to('merge', no_one)
|
||||
set_allowed_to('push', no_one)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do
|
|||
it "shows all dropdown sections in the 'Allowed to push' main dropdown, with only one deploy key" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
click_button 'Add protected branch'
|
||||
find(".js-allowed-to-push").click
|
||||
wait_for_requests
|
||||
|
||||
|
|
@ -35,6 +36,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do
|
|||
it "shows all sections but not deploy keys in the 'Allowed to merge' main dropdown" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
click_button 'Add protected branch'
|
||||
find(".js-allowed-to-merge").click
|
||||
wait_for_requests
|
||||
|
||||
|
|
@ -65,6 +67,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do
|
|||
it "just shows all sections but not deploy keys in the 'Allowed to push' dropdown" do
|
||||
visit project_protected_branches_path(project)
|
||||
|
||||
click_button 'Add protected branch'
|
||||
find(".js-allowed-to-push").click
|
||||
wait_for_requests
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue