Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
fb8ebc84b8
commit
9673d4228d
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlButton, GlIcon, GlLoadingIcon, GlPagination } from '@gitlab/ui';
|
||||
import { GlIcon, GlLoadingIcon, GlPagination } from '@gitlab/ui';
|
||||
import { createAlert } from '~/alert';
|
||||
import { s__, __ } from '~/locale';
|
||||
import { captureException } from '~/sentry/sentry_browser_wrapper';
|
||||
|
|
@ -21,7 +21,6 @@ export default {
|
|||
ConfirmModal,
|
||||
KeysPanel,
|
||||
NavigationTabs,
|
||||
GlButton,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
GlPagination,
|
||||
|
|
@ -90,7 +89,6 @@ export default {
|
|||
},
|
||||
i18n: {
|
||||
loading: s__('DeployKeys|Loading deploy keys'),
|
||||
addButton: s__('DeployKeys|Add new key'),
|
||||
prevPage: __('Go to previous page'),
|
||||
nextPage: __('Go to next page'),
|
||||
next: __('Next'),
|
||||
|
|
@ -172,7 +170,7 @@ export default {
|
|||
<template>
|
||||
<div class="deploy-keys">
|
||||
<confirm-modal :visible="confirmModalVisible" @remove="removeKey" @cancel="cancel" />
|
||||
<div class="gl-new-card-header gl-align-items-center gl-py-0 gl-pl-0">
|
||||
<div class="gl-items-center gl-py-0 gl-pl-0">
|
||||
<div class="top-area scrolling-tabs-container inner-page-scroll-tabs gl-border-b-0">
|
||||
<div class="fade-left">
|
||||
<gl-icon name="chevron-lg-left" :size="12" />
|
||||
|
|
@ -188,16 +186,6 @@ export default {
|
|||
@onChangeTab="onChangeTab"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="gl-new-card-actions">
|
||||
<gl-button
|
||||
size="small"
|
||||
class="js-toggle-button js-toggle-content"
|
||||
data-testid="add-new-deploy-key-button"
|
||||
>
|
||||
{{ $options.i18n.addButton }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</div>
|
||||
<gl-loading-icon
|
||||
v-if="$apollo.queries.deployKeys.loading"
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export default {
|
|||
<slot v-if="$scopedSlots.title" name="title"></slot>
|
||||
<template v-else>{{ title }}</template>
|
||||
</component>
|
||||
<p class="gl-text-secondary gl-m-0"><slot name="description"></slot></p>
|
||||
<p class="gl-text-subtle gl-m-0"><slot name="description"></slot></p>
|
||||
</div>
|
||||
<div class="gl-flex-shrink-0 gl-px-2">
|
||||
<gl-button
|
||||
|
|
|
|||
|
|
@ -18,11 +18,15 @@ export default {
|
|||
<section class="settings-section">
|
||||
<div class="settings-sticky-header">
|
||||
<div class="settings-sticky-header-inner">
|
||||
<h2 class="gl-heading-2 !gl-mb-2">
|
||||
<h2 class="gl-heading-2 !gl-mb-2" data-testid="settings-section-heading">
|
||||
<slot v-if="$scopedSlots.heading" name="heading"></slot>
|
||||
<template v-else>{{ heading }}</template>
|
||||
</h2>
|
||||
<p v-if="$scopedSlots.description || description" class="gl-text-secondary gl-mb-3">
|
||||
<p
|
||||
v-if="$scopedSlots.description || description"
|
||||
class="gl-text-subtle gl-mb-3"
|
||||
data-testid="settings-section-description"
|
||||
>
|
||||
<slot v-if="$scopedSlots.description" name="description"></slot>
|
||||
<template v-else>{{ description }}</template>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { isEmpty } from 'lodash';
|
||||
import { GlAlert, GlButton, GlTooltipDirective, GlEmptyState } from '@gitlab/ui';
|
||||
import noAccessSvg from '@gitlab/svgs/dist/illustrations/analytics/no-access.svg?raw';
|
||||
import noAccessSvg from '@gitlab/svgs/dist/illustrations/empty-state/empty-search-md.svg';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import { s__ } from '~/locale';
|
||||
import { getParameterByName, updateHistory, setUrlParams } from '~/lib/utils/url_utility';
|
||||
|
|
@ -249,9 +249,6 @@ export default {
|
|||
workItemIconName() {
|
||||
return this.workItem.workItemType?.iconName;
|
||||
},
|
||||
noAccessSvgPath() {
|
||||
return `data:image/svg+xml;utf8,${encodeURIComponent(noAccessSvg)}`;
|
||||
},
|
||||
hasDescriptionWidget() {
|
||||
return this.isWidgetPresent(WIDGET_TYPE_DESCRIPTION);
|
||||
},
|
||||
|
|
@ -480,6 +477,7 @@ export default {
|
|||
},
|
||||
WORK_ITEM_TYPE_VALUE_OBJECTIVE,
|
||||
WORKSPACE_PROJECT,
|
||||
noAccessSvg,
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -528,8 +526,7 @@ export default {
|
|||
v-else-if="error"
|
||||
:title="$options.i18n.fetchErrorTitle"
|
||||
:description="error"
|
||||
:svg-path="noAccessSvgPath"
|
||||
:svg-height="null"
|
||||
:svg-path="$options.noAccessSvg"
|
||||
/>
|
||||
<div v-else data-testid="detail-wrapper">
|
||||
<div class="gl-block sm:!gl-flex gl-items-start gl-flex-row gl-gap-3">
|
||||
|
|
|
|||
|
|
@ -5,25 +5,25 @@
|
|||
%h2.gl-text-base.gl-font-bold.gl-leading-24.gl-inline-flex.gl-gap-3.gl-m-0{ data: { testid: 'crud-title' } }
|
||||
= @title
|
||||
- if @count
|
||||
%span.gl-inline-flex.gl-items-center.gl-gap-2.gl-text-sm.gl-text-secondary{ data: { testid: 'crud-count' } }
|
||||
%span.gl-inline-flex.gl-items-center.gl-gap-2.gl-text-sm.gl-text-subtle{ data: { testid: 'crud-count' } }
|
||||
- if @icon
|
||||
= sprite_icon(@icon)
|
||||
%span{ class: @count_class }
|
||||
= @count
|
||||
- if description? || @description
|
||||
.gl-text-sm.gl-text-secondary.gl-mt-1.gl-mb-0{ data: { testid: 'crud-description' } }
|
||||
.gl-text-sm.gl-text-subtle.gl-mt-1.gl-mb-0{ data: { testid: 'crud-description' } }
|
||||
= description || @description
|
||||
.gl-flex.gl-gap-3.gl-items-baseline{ data: { testid: 'crud-actions' } }
|
||||
- if @toggle_text
|
||||
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-toggle-button js-toggle-content', data: { testid: 'crud-action-toggle' } }) do
|
||||
= render Pajamas::ButtonComponent.new(size: :small, button_options: button_options_attrs) do
|
||||
= @toggle_text
|
||||
= actions
|
||||
|
||||
- if form?
|
||||
.gl-p-5.gl-pt-4.gl-bg-default.gl-border-b.gl-border-default{ class: ('gl-hidden js-toggle-content' if @toggle_text), data: { testid: 'crud-form' } }
|
||||
.gl-p-5.gl-pt-4.gl-bg-default.gl-border-b.gl-border-default{ form_options_attrs }
|
||||
= form
|
||||
|
||||
.crud-body.gl-mx-5.gl-my-4{ class: ('gl-rounded-b-base' unless footer), data: { testid: 'crud-body' } }
|
||||
.crud-body.gl-mx-5.gl-my-4{ body_options_attrs }
|
||||
= body
|
||||
|
||||
- if footer?
|
||||
|
|
|
|||
|
|
@ -8,7 +8,14 @@ module Layouts
|
|||
# @param [String] icon
|
||||
# @param [String] toggle_text
|
||||
# @param [Hash] options
|
||||
def initialize(title, description: nil, count: nil, count_class: nil, icon: nil, toggle_text: nil, options: {})
|
||||
# @param [Hash] body_options
|
||||
# @param [Hash] form_options
|
||||
# @param [Hash] toggle_options
|
||||
def initialize(
|
||||
title, description: nil, count: nil, count_class: nil, icon: nil,
|
||||
toggle_text: nil, options: {}, body_options: {}, form_options: {},
|
||||
toggle_options: {}
|
||||
)
|
||||
@title = title
|
||||
@description = description
|
||||
@count = count
|
||||
|
|
@ -16,6 +23,9 @@ module Layouts
|
|||
@icon = icon
|
||||
@toggle_text = toggle_text
|
||||
@options = options
|
||||
@body_options = body_options
|
||||
@form_options = form_options
|
||||
@toggle_options = toggle_options
|
||||
end
|
||||
|
||||
renders_one :description
|
||||
|
|
@ -25,6 +35,42 @@ module Layouts
|
|||
renders_one :footer
|
||||
renders_one :pagination
|
||||
|
||||
def body_options_attrs
|
||||
default_testid = 'crud-body'
|
||||
default_classes = [
|
||||
('gl-rounded-b-base' unless footer)
|
||||
]
|
||||
@body_options.merge(default_attrs(@body_options, default_testid, default_classes))
|
||||
end
|
||||
|
||||
def button_options_attrs
|
||||
default_testid = 'crud-action-toggle'
|
||||
default_classes = ['js-toggle-button js-toggle-content']
|
||||
@toggle_options.merge(default_attrs(@toggle_options, default_testid, default_classes))
|
||||
end
|
||||
|
||||
def form_options_attrs
|
||||
default_testid = 'crud-form'
|
||||
default_classes = [
|
||||
('js-toggle-content' if @toggle_text),
|
||||
('gl-hidden' if @toggle_text && !@form_options[:class])
|
||||
]
|
||||
@form_options.merge(default_attrs(@form_options, default_testid, default_classes))
|
||||
end
|
||||
|
||||
delegate :sprite_icon, to: :helpers
|
||||
|
||||
private
|
||||
|
||||
def default_attrs(attrs, default_testid = nil, default_classes = [])
|
||||
data = attrs[:data] || {}
|
||||
data[:testid] = default_testid unless data[:testid]
|
||||
classes = attrs[:class] || ""
|
||||
|
||||
{
|
||||
data: data,
|
||||
class: "#{classes} #{default_classes.join(' ')}"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
%section{ class: section_classes, id: @id, data: (@testid ? { testid: @testid } : {}) }
|
||||
.gl-flex.gl-justify-between.gl-items-start.gl-pt-5
|
||||
.gl-flex.gl-justify-between.gl-items-start.gl-gap-x-3.gl-pt-5
|
||||
.gl-grow
|
||||
%h2{ class: title_classes }
|
||||
= heading || @heading
|
||||
- if description || @description
|
||||
%p.gl-text-secondary.gl-m-0
|
||||
%p.gl-text-subtle.gl-m-0
|
||||
= description || @description
|
||||
.gl-shrink-0.gl-px-2
|
||||
= render Pajamas::ButtonComponent.new(button_options: @button_options.merge(class: 'gl-min-w-12 js-settings-toggle')) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
%h2.gl-heading-2{ class: '!gl-mb-2' }
|
||||
= heading || @heading
|
||||
- if description || @description
|
||||
%p.gl-text-secondary.gl-mb-3
|
||||
%p.gl-text-subtle.gl-mb-3
|
||||
= description || @description
|
||||
%div{ data: { testid: 'settings-section-body' } }
|
||||
= body
|
||||
|
|
|
|||
|
|
@ -46,10 +46,20 @@ class Namespace
|
|||
%w[namespaces], url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424279'
|
||||
) do
|
||||
Namespace.transaction do
|
||||
@root.lock!("FOR NO KEY UPDATE")
|
||||
lock_options = 'FOR NO KEY UPDATE'
|
||||
lock_options += ' NOWAIT' if Feature.enabled?(:sync_traversal_ids_nowait, Feature.current_request)
|
||||
|
||||
@root.lock!(lock_options)
|
||||
|
||||
Namespace.connection.exec_query(sql)
|
||||
end
|
||||
end
|
||||
rescue ActiveRecord::LockWaitTimeout => e
|
||||
if e.message.starts_with? 'PG::LockNotAvailable'
|
||||
db_nowait_counter.increment(source: 'Namespace#sync_traversal_ids!')
|
||||
end
|
||||
|
||||
raise
|
||||
rescue ActiveRecord::Deadlocked
|
||||
db_deadlock_counter.increment(source: 'Namespace#sync_traversal_ids!')
|
||||
raise
|
||||
|
|
@ -95,6 +105,10 @@ class Namespace
|
|||
.find_by(parent_id: nil)
|
||||
end
|
||||
|
||||
def db_nowait_counter
|
||||
Gitlab::Metrics.counter(:db_nowait, 'Counts the times we triggered NOWAIT on a database lock operation')
|
||||
end
|
||||
|
||||
def db_deadlock_counter
|
||||
Gitlab::Metrics.counter(:db_deadlock, 'Counts the times we have deadlocked in the database')
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
%section.settings.as-default-branch-name.no-animate#js-default-branch-name{ class: ('expanded' if expanded_by_default?) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _('Default branch')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded_by_default? ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= s_('GroupSettings|Set the initial name and protections for the default branch of new repositories created in the group.')
|
||||
.settings-content
|
||||
= render ::Layouts::SettingsBlockComponent.new(_('Default branch'),
|
||||
id: 'js-default-branch-name',
|
||||
expanded: expanded_by_default?) do |c|
|
||||
- c.with_description do
|
||||
= s_('GroupSettings|Set the initial name and protections for the default branch of new repositories created in the group.')
|
||||
- c.with_body do
|
||||
= gitlab_ui_form_for @group, url: group_path(@group, anchor: 'js-default-branch-name'), html: { class: 'fieldset-form' } do |f|
|
||||
= form_errors(@group)
|
||||
- fallback_branch_name = "<code>#{Gitlab::DefaultBranch.value(object: @group)}</code>"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
- expanded = expanded_by_default?
|
||||
|
||||
%section.settings.no-animate#branch-defaults-settings{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Branch defaults')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= s_('ProjectSettings|Select the default branch for this project, and configure the template for branch names.')
|
||||
|
||||
.settings-content
|
||||
= render ::Layouts::SettingsBlockComponent.new(_('Branch defaults'),
|
||||
id: 'branch-defaults-settings',
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
= s_('ProjectSettings|Select the default branch for this project, and configure the template for branch names.')
|
||||
- c.with_body do
|
||||
- url = namespace_project_settings_repository_path(@project.namespace, @project)
|
||||
= gitlab_ui_form_for @project, url: url, method: :put, html: { multipart: true, class: "issue-settings-form js-issue-settings-form" }, authenticity_token: true do |f|
|
||||
%input{ name: 'update_section', type: 'hidden', value: 'js-issue-settings' }
|
||||
|
|
|
|||
|
|
@ -3,14 +3,12 @@
|
|||
- show_status_checks = @project.licensed_feature_available?(:external_status_checks)
|
||||
- show_approvers = @project.licensed_feature_available?(:merge_request_approvers)
|
||||
|
||||
%section.settings.no-animate#branch-rules{ class: ('expanded' if expanded), data: { testid: 'branch-rules-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Branch rules')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _('Define rules for who can push, merge, and the required approvals for each branch.')
|
||||
= link_to(_('Leave feedback.'), 'https://gitlab.com/gitlab-org/gitlab/-/issues/388149', target: '_blank', rel: 'noopener noreferrer')
|
||||
|
||||
.settings-content
|
||||
= render ::Layouts::SettingsBlockComponent.new(_('Branch rules'),
|
||||
id: 'branch-rules',
|
||||
testid: 'branch-rules-content',
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
= _('Define rules for who can push, merge, and the required approvals for each branch.')
|
||||
= link_to(_('Leave feedback.'), 'https://gitlab.com/gitlab-org/gitlab/-/issues/388149', target: '_blank', rel: 'noopener noreferrer')
|
||||
- c.with_body do
|
||||
#js-branch-rules{ data: { project_path: @project.full_path, branch_rules_path: project_settings_repository_branch_rules_path(@project), show_code_owners: show_code_owners.to_s, show_status_checks: show_status_checks.to_s, show_approvers: show_approvers.to_s } }
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
- expanded = expanded_by_default?
|
||||
|
||||
%section.settings.no-animate#cleanup{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Repository maintenance')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary.gl-pb-3
|
||||
= s_('ProjectMaintenance|Manage repository storage and cleanup.')
|
||||
.settings-content
|
||||
= render ::Layouts::SettingsBlockComponent.new(_('Repository maintenance'),
|
||||
id: 'cleanup',
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
= s_('ProjectMaintenance|Manage repository storage and cleanup.')
|
||||
- c.with_body do
|
||||
= render Pajamas::AlertComponent.new(variant: :danger, alert_options: { class: 'gl-mb-5' }, dismissible: false) do |c|
|
||||
- c.with_body do
|
||||
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
|
||||
|
|
|
|||
|
|
@ -1,56 +1,47 @@
|
|||
- expanded = expanded_by_default?
|
||||
- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
|
||||
- mirror_settings_enabled = can?(current_user, :admin_remote_mirror, @project)
|
||||
- mirror_settings_class = "#{'expanded' if expanded} #{'js-mirror-settings' if mirror_settings_enabled}".strip
|
||||
- mirror_settings_class = "#{'js-mirror-settings' if mirror_settings_enabled}".strip
|
||||
|
||||
%section.settings.project-mirror-settings.no-animate#js-push-remote-settings{ class: mirror_settings_class, data: { testid: 'mirroring-repositories-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Mirroring repositories')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _('Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically.')
|
||||
= link_to _('How do I mirror repositories?'), help_page_path('user/project/repository/mirror/index'), target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
|
||||
.settings-content
|
||||
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
|
||||
- c.with_header do
|
||||
.gl-new-card-title-wrapper
|
||||
%h5.gl-new-card-title
|
||||
= _('Mirrored repositories')
|
||||
.gl-new-card-count
|
||||
= sprite_icon('earth', css_class: 'gl-mr-2')
|
||||
%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", data: { testid: 'add-new-mirror' } }) do
|
||||
= _('Add new')
|
||||
= render ::Layouts::SettingsBlockComponent.new(_('Mirroring repositories'),
|
||||
id: 'js-push-remote-settings',
|
||||
testid: 'mirroring-repositories-settings-content',
|
||||
css_class: "project-mirror-settings #{mirror_settings_class}",
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
= _('Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically.')
|
||||
= link_to _('How do I mirror repositories?'), help_page_path('user/project/repository/mirror/index'), target: '_blank', rel: 'noopener noreferrer'
|
||||
- c.with_body do
|
||||
= render ::Layouts::CrudComponent.new(_('Mirrored repositories'),
|
||||
icon: 'earth',
|
||||
count: mirrored_repositories_count,
|
||||
toggle_text: _('Add new'),
|
||||
toggle_options: { data: { testid: 'add-new-mirror' } }) do |c|
|
||||
- c.with_body do
|
||||
- if mirror_settings_enabled
|
||||
.gl-new-card-add-form.gl-m-3.gl-mb-4.gl-display-none.js-toggle-content
|
||||
%h4.gl-mt-0
|
||||
= s_('Profiles|Add new mirror repository')
|
||||
= gitlab_ui_form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
|
||||
%div= form_errors(@project)
|
||||
.form-group.has-feedback
|
||||
= label_tag :url, _('Git repository URL'), class: 'label-light'
|
||||
= text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url gl-form-input-xl', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password', data: { testid: 'mirror-repository-url-field' }
|
||||
|
||||
= render 'projects/mirrors/instructions'
|
||||
|
||||
= render 'projects/mirrors/mirror_repos_form', f: f
|
||||
|
||||
= render 'projects/mirrors/branch_filter'
|
||||
|
||||
= f.submit _('Mirror repository'), class: 'js-mirror-submit', name: :update_remote_mirror, pajamas_button: true, data: { testid: 'mirror-repository-button' }
|
||||
|
||||
= render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-2 js-toggle-button' }) do
|
||||
= _('Cancel')
|
||||
|
||||
- else
|
||||
- unless mirror_settings_enabled
|
||||
= render Pajamas::AlertComponent.new(dismissible: false) do |c|
|
||||
- c.with_body do
|
||||
= _('Mirror settings are only available to GitLab administrators.')
|
||||
|
||||
= render 'projects/mirrors/mirror_repos_list'
|
||||
|
||||
- c.with_form do
|
||||
- if mirror_settings_enabled
|
||||
%h4.gl-mt-0
|
||||
= s_('Profiles|Add new mirror repository')
|
||||
= gitlab_ui_form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
|
||||
%div= form_errors(@project)
|
||||
.form-group.has-feedback
|
||||
= label_tag :url, _('Git repository URL'), class: 'label-light'
|
||||
= text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url gl-form-input-xl', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password', data: { testid: 'mirror-repository-url-field' }
|
||||
|
||||
= render 'projects/mirrors/instructions'
|
||||
|
||||
= render 'projects/mirrors/mirror_repos_form', f: f
|
||||
|
||||
= render 'projects/mirrors/branch_filter'
|
||||
|
||||
= f.submit _('Mirror repository'), class: 'js-mirror-submit', name: :update_remote_mirror, pajamas_button: true, data: { testid: 'mirror-repository-button' }
|
||||
|
||||
= render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-2 js-toggle-button' }) do
|
||||
= _('Cancel')
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.table-responsive.gl-mb-0
|
||||
- if !@project.mirror? && @project.remote_mirrors.count == 0
|
||||
.gl-new-card-empty.gl-px-5.gl-py-4= _('There are currently no mirrored repositories.')
|
||||
.gl-text-subtle= _('There are currently no mirrored repositories.')
|
||||
- else
|
||||
%table.table.b-table.gl-table.b-table-stacked-md
|
||||
%thead.gl-hidden.md:gl-table-header-group
|
||||
|
|
|
|||
|
|
@ -1,35 +1,25 @@
|
|||
- expanded = expanded_by_default?
|
||||
|
||||
%section.settings.no-animate#js-protected-tags-settings{ class: ('expanded' if expanded), data: { testid: 'protected-tag-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= s_("ProtectedTag|Protected tags")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= s_("ProtectedTag|Limit access to creating and updating tags.")
|
||||
= link_to s_("ProtectedTag|What are protected tags?"), help_page_path("user/project/protected_tags")
|
||||
.settings-content
|
||||
= render ::Layouts::SettingsBlockComponent.new(s_("ProtectedTag|Protected tags"),
|
||||
id: 'js-protected-tags-settings',
|
||||
testid: 'protected-tag-settings-content',
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
= s_("ProtectedTag|Limit access to creating and updating tags.")
|
||||
= link_to s_("ProtectedTag|What are protected tags?"), help_page_path("user/project/protected_tags")
|
||||
- c.with_body do
|
||||
%p.gl-text-secondary
|
||||
= s_("ProtectedTag|By default, protected tags restrict who can modify the tag.")
|
||||
= link_to s_("ProtectedTag|Learn more."), help_page_path("user/project/protected_tags", anchor: "who-can-modify-a-protected-tag")
|
||||
|
||||
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
|
||||
- c.with_header do
|
||||
.gl-new-card-title-wrapper
|
||||
%h3.gl-new-card-title
|
||||
= _('Protected tags')
|
||||
.gl-new-card-count
|
||||
= sprite_icon('tag', css_class: 'gl-mr-2')
|
||||
= @protected_tags_count
|
||||
- if can? current_user, :admin_project, @project
|
||||
.gl-new-card-actions
|
||||
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content" }) do
|
||||
= _('Add tag')
|
||||
= render ::Layouts::CrudComponent.new(_('Protected tags'),
|
||||
icon: 'tag',
|
||||
count: @protected_tags_count,
|
||||
toggle_text: _('Add tag')) do |c|
|
||||
- c.with_body do
|
||||
- if can? current_user, :admin_project, @project
|
||||
.gl-new-card-add-form.gl-m-3.gl-mb-4.gl-display-none.js-toggle-content
|
||||
%h4.gl-mt-0
|
||||
= _('Protect a tag')
|
||||
= yield :create_protected_tag
|
||||
= yield :tag_list
|
||||
- c.with_form do
|
||||
- if can? current_user, :admin_project, @project
|
||||
%h4.gl-mt-0
|
||||
= _('Protect a tag')
|
||||
= yield :create_protected_tag
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.protected-tags-list.js-protected-tags-list
|
||||
- if @protected_tags.empty?
|
||||
.gl-new-card-empty.gl-px-5.gl-py-4
|
||||
.gl-text-subtle
|
||||
= s_('ProtectedBranch|No tags are protected.')
|
||||
- else
|
||||
- can_admin_project = can?(current_user, :admin_project, @project)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.protected-branches-list.js-protected-branches-list{ data: { testid: 'protected-branches-list' } }
|
||||
- if @protected_branches.empty?
|
||||
%p.gl-new-card-empty.gl-px-5.gl-py-4.js-toggle-content
|
||||
%p.gl-text-subtle
|
||||
= s_("ProtectedBranch|There are currently no protected branches, to protect a branch start by creating a new one above.")
|
||||
- else
|
||||
.flash-container
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
- can_admin_entity = protected_branch_can_admin_entity?(protected_branch_entity)
|
||||
- expanded = expanded_by_default?
|
||||
|
||||
%section.settings.no-animate#js-protected-branches-settings{ class: ('expanded' if expanded), data: { testid: 'protected-branches-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= s_("ProtectedBranch|Protected branches")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= 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
|
||||
= render ::Layouts::SettingsBlockComponent.new(s_("ProtectedBranch|Protected branches"),
|
||||
id: 'js-protected-branches-settings',
|
||||
testid: 'protected-branches-settings-content',
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
= 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")
|
||||
- c.with_body do
|
||||
.js-alert-protected-branch-created-container.gl-mt-5
|
||||
|
||||
= render Pajamas::AlertComponent.new(variant: :warning,
|
||||
|
|
@ -20,25 +18,20 @@
|
|||
= s_("ProtectedBranch|Giving merge rights to a protected branch also gives elevated permissions for certain CI/CD features.")
|
||||
= link_to s_("ProtectedBranch|What are the security implications?"), help_page_path('ci/pipelines/index', anchor: 'pipeline-security-on-protected-branches'), target: '_blank', rel: 'noopener noreferrer'
|
||||
|
||||
= 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-protected-branch-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")
|
||||
= render ::Layouts::CrudComponent.new(s_("ProtectedBranch|Protected branches"),
|
||||
icon: 'branch',
|
||||
count: @protected_branches.size,
|
||||
toggle_text: _('Add protected branch'),
|
||||
toggle_options: { data: { testid: 'add-protected-branch-button' } }) do |c|
|
||||
- c.with_description do
|
||||
= 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
|
||||
|
||||
= paginate @protected_branches, theme: 'gitlab'
|
||||
- c.with_form do
|
||||
- if can_admin_entity
|
||||
= content_for :create_protected_branch
|
||||
|
||||
- c.with_pagination do
|
||||
= paginate @protected_branches, theme: 'gitlab'
|
||||
|
|
|
|||
|
|
@ -1,21 +1,25 @@
|
|||
- expanded = expanded_by_default?
|
||||
%section.rspec-deploy-keys-settings.settings.no-animate#js-deploy-keys-settings{ class: ('expanded' if expanded), data: { testid: 'deploy-keys-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Deploy keys')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
- link = link_to('', help_page_path('user/project/deploy_keys/index'), target: '_blank', rel: 'noopener noreferrer')
|
||||
= safe_format(_("Add deploy keys to grant read/write access to this repository. %{link_start}What are deploy keys?%{link_end}"), tag_pair(link, :link_start, :link_end))
|
||||
.settings-content
|
||||
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
|
||||
- c.with_body do
|
||||
.gl-new-card-add-form.gl-m-3.gl-display-none.js-toggle-content
|
||||
= render @deploy_keys.form_partial_path
|
||||
|
||||
= render ::Layouts::SettingsBlockComponent.new(_('Deploy keys'),
|
||||
id: 'js-deploy-keys-settings',
|
||||
testid: 'deploy-keys-settings-content',
|
||||
css_class: 'rspec-deploy-keys-settings',
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
- link = link_to('', help_page_path('user/project/deploy_keys/index'), target: '_blank', rel: 'noopener noreferrer')
|
||||
= safe_format(_("Add deploy keys to grant read/write access to this repository. %{link_start}What are deploy keys?%{link_end}"), tag_pair(link, :link_start, :link_end))
|
||||
- c.with_body do
|
||||
= render ::Layouts::CrudComponent.new(_('Deploy keys'),
|
||||
body_options: { class: '!gl-m-0' },
|
||||
toggle_text: s_('DeployKeys|Add new key'),
|
||||
toggle_options: { data: { testid: 'add-new-deploy-key-button' } }) do |c|
|
||||
- c.with_body do
|
||||
#js-deploy-keys{ data: { project_id: @project.id,
|
||||
project_path: @project.full_path,
|
||||
enabled_endpoint: enabled_keys_project_deploy_keys_path(@project),
|
||||
available_project_endpoint: available_project_keys_project_deploy_keys_path(@project),
|
||||
available_public_endpoint: available_public_keys_project_deploy_keys_path(@project)
|
||||
} }
|
||||
|
||||
- c.with_form do
|
||||
= render @deploy_keys.form_partial_path
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
- expanded = expand_deploy_tokens_section?(@new_deploy_token, @created_deploy_token)
|
||||
|
||||
%section.settings.no-animate#js-deploy-tokens{ class: ('expanded' if expanded), data: { testid: 'deploy-tokens-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= s_('DeployTokens|Deploy tokens')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= description
|
||||
.settings-content
|
||||
= render ::Layouts::SettingsBlockComponent.new(s_('DeployTokens|Deploy tokens'),
|
||||
id: 'js-deploy-tokens',
|
||||
testid: 'deploy-tokens-settings-content',
|
||||
expanded: expanded) do |c|
|
||||
- c.with_description do
|
||||
= description
|
||||
- c.with_body do
|
||||
#new-deploy-token-alert
|
||||
= render 'shared/deploy_tokens/table', group_or_project: group_or_project, active_tokens: @deploy_tokens
|
||||
|
|
|
|||
|
|
@ -1,24 +1,8 @@
|
|||
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
|
||||
- c.with_header do
|
||||
.gl-new-card-title-wrapper
|
||||
%h5.gl-new-card-title
|
||||
= s_("DeployTokens|Active deploy tokens")
|
||||
.gl-new-card-count
|
||||
= sprite_icon('token', css_class: 'gl-mr-2')
|
||||
= active_tokens.length
|
||||
.gl-new-card-actions
|
||||
= render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content" }) do
|
||||
= _('Add token')
|
||||
= render ::Layouts::CrudComponent.new(s_("DeployTokens|Active deploy tokens"),
|
||||
icon: 'token',
|
||||
count: active_tokens.length,
|
||||
toggle_text: _('Add token')) do |c|
|
||||
- c.with_body do
|
||||
.gl-new-card-add-form.gl-m-3.gl-mb-4.gl-display-none.js-toggle-content
|
||||
#js-new-deploy-token{ data: {
|
||||
container_registry_enabled: container_registry_enabled?(group_or_project),
|
||||
packages_registry_enabled: packages_registry_enabled?(group_or_project),
|
||||
create_new_token_path: create_deploy_token_path(group_or_project),
|
||||
token_type: group_or_project.is_a?(Group) ? 'group' : 'project',
|
||||
deploy_tokens_help_url: help_page_path('user/project/deploy_tokens/index')
|
||||
}
|
||||
}
|
||||
- if active_tokens.present?
|
||||
%table.table.b-table.gl-table.b-table-stacked-md
|
||||
%thead
|
||||
|
|
@ -50,5 +34,14 @@
|
|||
.js-deploy-token-revoke-button{ data: deploy_token_revoke_button_data(token: token, group_or_project: group_or_project) }
|
||||
|
||||
- else
|
||||
.gl-new-card-empty.gl-px-5.gl-py-4
|
||||
.gl-text-subtle
|
||||
= s_('DeployTokens|This %{entity_type} has no active deploy tokens.') % { entity_type: group_or_project.class.name.downcase }
|
||||
- c.with_form do
|
||||
#js-new-deploy-token{ data: {
|
||||
container_registry_enabled: container_registry_enabled?(group_or_project),
|
||||
packages_registry_enabled: packages_registry_enabled?(group_or_project),
|
||||
create_new_token_path: create_deploy_token_path(group_or_project),
|
||||
token_type: group_or_project.is_a?(Group) ? 'group' : 'project',
|
||||
deploy_tokens_help_url: help_page_path('user/project/deploy_tokens/index')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
name: use_sonnet_35
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/468334
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157696
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/469499
|
||||
milestone: '17.2'
|
||||
group: group::ai framework
|
||||
type: beta
|
||||
default_enabled: true
|
||||
name: sync_traversal_ids_nowait
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/468848
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/158024
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/472155
|
||||
milestone: '17.3'
|
||||
group: group::tenant scale
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveOldCodeSuggestionEventsCronWorker < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
milestone '17.3'
|
||||
|
||||
WORKER_CLASS = 'ClickHouse::CodeSuggestionEventsCronWorker'
|
||||
|
||||
def up
|
||||
# TODO: make shard-aware. See https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/3430
|
||||
Gitlab::SidekiqSharding::Validator.allow_unrouted_sidekiq_calls do
|
||||
Sidekiq::Cron::Job.destroy('click_house_code_suggestion_events_cron_worker')
|
||||
end
|
||||
|
||||
sidekiq_remove_jobs(job_klasses: [WORKER_CLASS])
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
8e40a312b2615cb6677b8ba3c13c7ad339c61bead58ebacf18d985d873ab0a11
|
||||
|
|
@ -46,7 +46,7 @@ The workaround is to set your variables in [GitLab CI/CD variables](../variables
|
|||
POSTGRES_DB: $POSTGRES_DB
|
||||
POSTGRES_USER: $POSTGRES_USER
|
||||
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
```
|
||||
|
||||
For more information about using `postgres` for the `Host`, see [How services are linked to the job](../services/index.md#how-services-are-linked-to-the-job).
|
||||
|
|
|
|||
|
|
@ -86,6 +86,9 @@ that were found on the branch it was run on.
|
|||
DETAILS:
|
||||
**Tier:** Ultimate
|
||||
**Offering:** GitLab.com, Self-managed
|
||||
**Status:** Beta
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72724) in GitLab 14.5 [with a flag](../../administration/feature_flags.md) named `project_quality_summary_page`. This feature is in [beta](../../policy/experiment-beta-support.md). Disabled by default.
|
||||
|
||||
The project quality view displays an overview of the code quality findings. The view can be found under **Analyze > CI/CD analytics**, and requires [`project_quality_summary_page`](../../user/feature_flags.md) feature flag to be enabled for this particular project.
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,11 @@ Examples:
|
|||
| `compliance_frameworks` | `array` | | List of IDs of the compliance frameworks in scope of enforcement, in an array of objects with key `id`. |
|
||||
| `projects` | `object` | `including`, `excluding` | Use `excluding:` or `including:` then list the IDs of the projects you wish to include or exclude, in an array of objects with key `id`. |
|
||||
|
||||
### Example security policies project
|
||||
### Examples
|
||||
|
||||
These examples demonstrate what you can achieve with pipeline execution policies.
|
||||
|
||||
#### Pipeline execution policy
|
||||
|
||||
You can use the following example in a `.gitlab/security-policies/policy.yml` file stored in a
|
||||
[security policy project](index.md#security-policy-project):
|
||||
|
|
@ -108,3 +112,27 @@ pipeline_execution_policy:
|
|||
including:
|
||||
- id: 361
|
||||
```
|
||||
|
||||
##### Customize enforced jobs based on project variables
|
||||
|
||||
You can customize enforced jobs, based on the presence of a project variable. In this example,
|
||||
the value of `CS_IMAGE` is defined in the policy as `alpine:latest`. However, if the project
|
||||
also defines the value of `CS_IMAGE`, that value is used instead. The CI/CD variable must be a
|
||||
predefined project variable, not defined in the project's `.gitlab-ci.yml` file.
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
CS_ANALYZER_IMAGE: "$CI_TEMPLATE_REGISTRY_HOST/security-products/container-scanning:7"
|
||||
CS_IMAGE: alpine:latest
|
||||
|
||||
policy::container-security:
|
||||
stage: .pipeline-policy-pre
|
||||
rules:
|
||||
- if: $CS_IMAGE
|
||||
variables:
|
||||
CS_IMAGE: $PROJECT_CS_IMAGE
|
||||
- when: always
|
||||
script:
|
||||
- echo "CS_ANALYZER_IMAGE:$CS_ANALYZER_IMAGE"
|
||||
- echo "CS_IMAGE:$CS_IMAGE"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ module QA
|
|||
module Project
|
||||
module Settings
|
||||
class DeployKeys < Page::Base
|
||||
view 'app/views/shared/deploy_keys/_index.html.haml' do
|
||||
element 'add-new-deploy-key-button'
|
||||
end
|
||||
|
||||
view 'app/views/shared/deploy_keys/_form.html.haml' do
|
||||
element 'deploy-key-title-field'
|
||||
element 'deploy-key-field'
|
||||
|
|
@ -19,7 +23,6 @@ module QA
|
|||
|
||||
view 'app/assets/javascripts/deploy_keys/components/app.vue' do
|
||||
element 'project-deploy-keys-container'
|
||||
element 'add-new-deploy-key-button'
|
||||
end
|
||||
|
||||
view 'app/assets/javascripts/deploy_keys/components/key.vue' do
|
||||
|
|
|
|||
|
|
@ -51,6 +51,15 @@ RSpec.describe Layouts::CrudComponent, type: :component, feature_category: :shar
|
|||
expect(page).to have_css('[data-testid="crud-action-toggle"].js-toggle-button.js-toggle-content')
|
||||
end
|
||||
|
||||
it 'renders action toggle custom attributes' do
|
||||
render_inline described_class.new(title,
|
||||
toggle_text: toggle_text,
|
||||
toggle_options: { class: 'custom-button-class', data: { testid: 'crud-custom-toggle-id' } })
|
||||
|
||||
expect(page).to have_css('.custom-button-class', text: toggle_text)
|
||||
expect(page).to have_css('[data-testid="crud-custom-toggle-id"]', text: toggle_text)
|
||||
end
|
||||
|
||||
it 'renders actions slot' do
|
||||
render_inline component_title do |c|
|
||||
c.with_actions { actions }
|
||||
|
|
@ -59,12 +68,23 @@ RSpec.describe Layouts::CrudComponent, type: :component, feature_category: :shar
|
|||
expect(page).to have_css('[data-testid="crud-actions"]', text: actions)
|
||||
end
|
||||
|
||||
it 'renders form slot' do
|
||||
render_inline component_title do |c|
|
||||
it 'renders hidden form slot if toggle is set' do
|
||||
render_inline described_class.new(title, toggle_text: toggle_text) do |c|
|
||||
c.with_form { form }
|
||||
end
|
||||
|
||||
expect(page).to have_css('[data-testid="crud-form"]', text: form)
|
||||
expect(page).to have_css('.gl-hidden', text: form)
|
||||
end
|
||||
|
||||
it 'renders form custom attributes' do
|
||||
render_inline described_class.new(title,
|
||||
form_options: { class: 'error-class', data: { testid: 'crud-custom-form-id' } }) do |c|
|
||||
c.with_form { form }
|
||||
end
|
||||
|
||||
expect(page).to have_css('.error-class', text: form)
|
||||
expect(page).not_to have_css('.gl-hidden', text: form)
|
||||
expect(page).to have_css('[data-testid="crud-custom-form-id"]', text: form)
|
||||
end
|
||||
|
||||
it 'renders body slot' do
|
||||
|
|
@ -75,6 +95,16 @@ RSpec.describe Layouts::CrudComponent, type: :component, feature_category: :shar
|
|||
expect(page).to have_css('[data-testid="crud-body"]', text: body)
|
||||
end
|
||||
|
||||
it 'renders body custom attributes' do
|
||||
render_inline described_class.new(title,
|
||||
body_options: { class: '!gl-m-0', data: { testid: 'crud-custom-body-id' } }) do |c|
|
||||
c.with_body { body }
|
||||
end
|
||||
|
||||
expect(page).to have_css('.\!gl-m-0', text: body)
|
||||
expect(page).to have_css('[data-testid="crud-custom-body-id"]', text: body)
|
||||
end
|
||||
|
||||
it 'renders footer slot' do
|
||||
render_inline component_title do |c|
|
||||
c.with_footer { footer }
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ RSpec.describe Layouts::SettingsBlockComponent, type: :component, feature_catego
|
|||
it 'renders description' do
|
||||
render_inline described_class.new(heading, description: description)
|
||||
|
||||
expect(page).to have_css('.gl-text-secondary', text: description)
|
||||
expect(page).to have_css('.gl-text-subtle', text: description)
|
||||
end
|
||||
|
||||
it 'renders description slot' do
|
||||
|
|
@ -27,7 +27,7 @@ RSpec.describe Layouts::SettingsBlockComponent, type: :component, feature_catego
|
|||
c.with_description { description }
|
||||
end
|
||||
|
||||
expect(page).to have_css('.gl-text-secondary', text: description)
|
||||
expect(page).to have_css('.gl-text-subtle', text: description)
|
||||
end
|
||||
|
||||
it 'renders body slot' do
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ RSpec.describe Layouts::SettingsSectionComponent, type: :component, feature_cate
|
|||
it 'renders description' do
|
||||
render_inline described_class.new(heading, description: description)
|
||||
|
||||
expect(page).to have_css('.gl-text-secondary', text: description)
|
||||
expect(page).to have_css('.gl-text-subtle', text: description)
|
||||
end
|
||||
|
||||
it 'renders description slot' do
|
||||
|
|
@ -27,7 +27,7 @@ RSpec.describe Layouts::SettingsSectionComponent, type: :component, feature_cate
|
|||
c.with_description { description }
|
||||
end
|
||||
|
||||
expect(page).to have_css('.gl-text-secondary', text: description)
|
||||
expect(page).to have_css('.gl-text-subtle', text: description)
|
||||
end
|
||||
|
||||
it 'renders body slot' do
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ RSpec.describe 'Projects > Settings > Repository settings', feature_category: :s
|
|||
end
|
||||
|
||||
it 'hides remote mirror settings' do
|
||||
expect(page.find('.project-mirror-settings')).not_to have_selector('form')
|
||||
expect(find_by_testid('mirroring-repositories-settings-content')).not_to have_selector('form')
|
||||
expect(page).to have_content('Mirror settings are only available to GitLab administrators.')
|
||||
end
|
||||
end
|
||||
|
|
@ -337,7 +337,7 @@ RSpec.describe 'Projects > Settings > Repository settings', feature_category: :s
|
|||
context 'for admin' do
|
||||
shared_examples_for 'shows mirror settings' do
|
||||
it 'shows mirror settings' do
|
||||
expect(page.find('.project-mirror-settings')).to have_selector('form')
|
||||
expect(find_by_testid('mirroring-repositories-settings-content')).to have_selector('form')
|
||||
expect(page).not_to have_content('Changing mirroring setting is disabled for non-admin users.')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -122,11 +122,11 @@ RSpec.describe "User interacts with deploy keys", :js, feature_category: :contin
|
|||
it 'click on cancel hides the form' do
|
||||
click_button('Add new key')
|
||||
|
||||
expect(page).to have_css('.gl-new-card-add-form')
|
||||
expect(page).to have_css('[data-testid="crud-form"]')
|
||||
|
||||
click_button('Cancel')
|
||||
|
||||
expect(page).not_to have_css('.gl-new-card-add-form')
|
||||
expect(page).not_to have_css('[data-testid="crud-form"]')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ RSpec.describe 'Protected Tags', :js, :with_license, feature_category: :source_c
|
|||
set_allowed_to('create')
|
||||
click_on_protect
|
||||
|
||||
within("#js-protected-tags-settings .gl-new-card-count") do
|
||||
within('#js-protected-tags-settings [data-testid="crud-count"]') do
|
||||
expect(page).to have_content("2")
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ describe('Settings Block', () => {
|
|||
wrapper = mountExtended(SettingsSection, {
|
||||
propsData,
|
||||
slots: {
|
||||
heading: '<div data-testid="heading-slot">Advanced</div>',
|
||||
description: '<div data-testid="description-slot"></div>',
|
||||
heading: '<div data-testid="heading-slot">Heading</div>',
|
||||
description: '<div data-testid="description-slot">Description</div>',
|
||||
default: '<div data-testid="default-slot"></div>',
|
||||
},
|
||||
});
|
||||
|
|
@ -17,7 +17,9 @@ describe('Settings Block', () => {
|
|||
|
||||
const findDefaultSlot = () => wrapper.findByTestId('default-slot');
|
||||
const findHeadingSlot = () => wrapper.findByTestId('heading-slot');
|
||||
const findHeading = () => wrapper.findByTestId('settings-section-heading');
|
||||
const findDescriptionSlot = () => wrapper.findByTestId('description-slot');
|
||||
const findDescription = () => wrapper.findByTestId('settings-section-description');
|
||||
|
||||
it('has a default slot', () => {
|
||||
mountComponent();
|
||||
|
|
@ -31,9 +33,23 @@ describe('Settings Block', () => {
|
|||
expect(findHeadingSlot().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('has correct heading text and classes', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findHeading().text()).toBe('Heading');
|
||||
expect(findHeading().classes()).toEqual(['gl-heading-2', '!gl-mb-2']);
|
||||
});
|
||||
|
||||
it('has a description slot', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findDescriptionSlot().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('has correct description text and classes', () => {
|
||||
mountComponent();
|
||||
|
||||
expect(findDescription().text()).toBe('Description');
|
||||
expect(findDescription().classes()).toEqual(['gl-text-subtle', 'gl-mb-3']);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ RSpec.describe Banzai::Filter::EmojiFilter, feature_category: :team_planning do
|
|||
end
|
||||
end
|
||||
|
||||
it 'limit keeps it from timing out' do
|
||||
it 'limit keeps it from timing out', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/454749' do
|
||||
expect do
|
||||
Timeout.timeout(1.second) { filter('⏯ :play_pause: ' * 500000) }
|
||||
end.not_to raise_error
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ RSpec.describe Namespace::TraversalHierarchy, type: :model, feature_category: :g
|
|||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'locked row' do
|
||||
it_behaves_like 'locked row', nowait: true do
|
||||
let(:recorded_queries) { ActiveRecord::QueryRecorder.new }
|
||||
let(:row) { root }
|
||||
|
||||
|
|
@ -77,6 +77,38 @@ RSpec.describe Namespace::TraversalHierarchy, type: :model, feature_category: :g
|
|||
end
|
||||
end
|
||||
|
||||
context 'when record is already locked' do
|
||||
before do
|
||||
msg = %(PG::LockNotAvailable: ERROR: could not obtain lock on row in relation "namespaces"\n)
|
||||
allow(root).to receive(:lock!).and_raise(ActiveRecord::LockWaitTimeout.new(msg))
|
||||
end
|
||||
|
||||
it { expect { subject }.to raise_error(ActiveRecord::LockWaitTimeout) }
|
||||
|
||||
it 'increment db_nowait counter' do
|
||||
expect do
|
||||
subject
|
||||
rescue StandardError
|
||||
nil
|
||||
end.to change { db_nowait_total('Namespace#sync_traversal_ids!') }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sync_traversal_ids_nowait feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(sync_traversal_ids_nowait: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'locked row', nowait: false do
|
||||
let(:recorded_queries) { ActiveRecord::QueryRecorder.new }
|
||||
let(:row) { root }
|
||||
|
||||
before do
|
||||
recorded_queries.record { subject }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when deadlocked' do
|
||||
before do
|
||||
allow(root).to receive(:lock!) { raise ActiveRecord::Deadlocked }
|
||||
|
|
@ -94,6 +126,12 @@ RSpec.describe Namespace::TraversalHierarchy, type: :model, feature_category: :g
|
|||
end
|
||||
end
|
||||
|
||||
def db_nowait_total(source)
|
||||
Gitlab::Metrics
|
||||
.counter(:db_nowait, 'Counts the times we triggered NOWAIT on a database lock operation')
|
||||
.get(source: source)
|
||||
end
|
||||
|
||||
def db_deadlock_total(source)
|
||||
Gitlab::Metrics
|
||||
.counter(:db_deadlock, 'Counts the times we have deadlocked in the database')
|
||||
|
|
|
|||
|
|
@ -4,17 +4,19 @@
|
|||
# Ensure a transaction also occurred.
|
||||
# Be careful! This form of spec is not foolproof, but better than nothing.
|
||||
|
||||
RSpec.shared_examples 'locked row' do
|
||||
RSpec.shared_examples 'locked row' do |nowait: false|
|
||||
it "has locked row" do
|
||||
table_name = row.class.table_name
|
||||
ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+FOR NO KEY UPDATE/m
|
||||
|
||||
expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT'
|
||||
expect(recorded_queries.log).to include a_string_matching ids_regex
|
||||
|
||||
expect(recorded_queries.log).to include a_string_matching 'NOWAIT' if nowait
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'locked rows' do
|
||||
RSpec.shared_examples 'locked rows' do |nowait: false|
|
||||
it "has locked rows" do
|
||||
table_name = rows.first.class.table_name
|
||||
|
||||
|
|
@ -23,5 +25,7 @@ RSpec.shared_examples 'locked rows' do
|
|||
|
||||
expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT'
|
||||
expect(recorded_queries.log).to include a_string_matching ids_regex
|
||||
|
||||
expect(recorded_queries.log).to include a_string_matching 'NOWAIT' if nowait
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue