From 9673d4228d0e4f4c395a7110ccfbdefa71ac1653 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 24 Jul 2024 06:08:43 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../deploy_keys/components/app.vue | 16 +--- .../components/settings/settings_block.vue | 2 +- .../components/settings/settings_section.vue | 8 +- .../components/work_item_detail.vue | 9 +- app/components/layouts/crud_component.haml | 10 +-- app/components/layouts/crud_component.rb | 48 ++++++++++- .../layouts/settings_block_component.haml | 4 +- .../layouts/settings_section_component.haml | 2 +- app/models/namespace/traversal_hierarchy.rb | 16 +++- .../repository/_default_branch.html.haml | 15 ++-- .../projects/branch_defaults/_show.html.haml | 15 ++-- .../projects/branch_rules/_show.html.haml | 18 ++-- .../projects/maintenance/_show.html.haml | 14 ++-- .../projects/mirrors/_mirror_repos.html.haml | 83 +++++++++---------- .../mirrors/_mirror_repos_list.html.haml | 2 +- .../protected_tags/shared/_index.html.haml | 44 ++++------ .../shared/_tags_list.html.haml | 2 +- .../shared/_branches_list.html.haml | 2 +- .../shared/_index.html.haml | 51 +++++------- app/views/shared/deploy_keys/_index.html.haml | 30 ++++--- .../shared/deploy_tokens/_index.html.haml | 15 ++-- .../shared/deploy_tokens/_table.html.haml | 35 ++++---- .../sync_traversal_ids_nowait.yml} | 16 ++-- ..._old_code_suggestion_events_cron_worker.rb | 22 +++++ db/schema_migrations/20240723102617 | 1 + doc/ci/services/postgres.md | 2 +- doc/ci/testing/code_quality.md | 3 + .../policies/pipeline_execution_policies.md | 30 ++++++- qa/qa/page/project/settings/deploy_keys.rb | 5 +- .../components/layouts/crud_component_spec.rb | 36 +++++++- .../layouts/settings_block_component_spec.rb | 4 +- .../settings_section_component_spec.rb | 4 +- .../settings/repository_settings_spec.rb | 4 +- .../user_interacts_with_deploy_keys_spec.rb | 4 +- spec/features/protected_tags_spec.rb | 2 +- .../settings/settings_section_spec.js | 20 ++++- spec/lib/banzai/filter/emoji_filter_spec.rb | 2 +- .../namespace/traversal_hierarchy_spec.rb | 40 ++++++++- .../row_lock_shared_examples.rb | 8 +- 39 files changed, 399 insertions(+), 245 deletions(-) rename config/feature_flags/{beta/use_sonnet_35.yml => gitlab_com_derisk/sync_traversal_ids_nowait.yml} (55%) create mode 100644 db/migrate/20240723102617_remove_old_code_suggestion_events_cron_worker.rb create mode 100644 db/schema_migrations/20240723102617 diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index 7e93f7b9295..256fdedc7ab 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -1,5 +1,5 @@ @@ -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" />
diff --git a/app/components/layouts/crud_component.haml b/app/components/layouts/crud_component.haml index f9c49c31b2e..8dfdbed6f46 100644 --- a/app/components/layouts/crud_component.haml +++ b/app/components/layouts/crud_component.haml @@ -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? diff --git a/app/components/layouts/crud_component.rb b/app/components/layouts/crud_component.rb index 966a0fd88d3..280dd942f04 100644 --- a/app/components/layouts/crud_component.rb +++ b/app/components/layouts/crud_component.rb @@ -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 diff --git a/app/components/layouts/settings_block_component.haml b/app/components/layouts/settings_block_component.haml index 40ee64c449c..9e93eb4557f 100644 --- a/app/components/layouts/settings_block_component.haml +++ b/app/components/layouts/settings_block_component.haml @@ -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 diff --git a/app/components/layouts/settings_section_component.haml b/app/components/layouts/settings_section_component.haml index 8872473d482..b21d1415745 100644 --- a/app/components/layouts/settings_section_component.haml +++ b/app/components/layouts/settings_section_component.haml @@ -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 diff --git a/app/models/namespace/traversal_hierarchy.rb b/app/models/namespace/traversal_hierarchy.rb index 86fb562f4f4..66f40fadf05 100644 --- a/app/models/namespace/traversal_hierarchy.rb +++ b/app/models/namespace/traversal_hierarchy.rb @@ -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 diff --git a/app/views/groups/settings/repository/_default_branch.html.haml b/app/views/groups/settings/repository/_default_branch.html.haml index ce51edc4f3f..c5f281f852e 100644 --- a/app/views/groups/settings/repository/_default_branch.html.haml +++ b/app/views/groups/settings/repository/_default_branch.html.haml @@ -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 = "#{Gitlab::DefaultBranch.value(object: @group)}" diff --git a/app/views/projects/branch_defaults/_show.html.haml b/app/views/projects/branch_defaults/_show.html.haml index 521d5bb9890..4119bf015e3 100644 --- a/app/views/projects/branch_defaults/_show.html.haml +++ b/app/views/projects/branch_defaults/_show.html.haml @@ -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' } diff --git a/app/views/projects/branch_rules/_show.html.haml b/app/views/projects/branch_rules/_show.html.haml index 10cb91e35bd..28e7ef96741 100644 --- a/app/views/projects/branch_rules/_show.html.haml +++ b/app/views/projects/branch_rules/_show.html.haml @@ -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 } } diff --git a/app/views/projects/maintenance/_show.html.haml b/app/views/projects/maintenance/_show.html.haml index 2c726130614..42590dd69ff 100644 --- a/app/views/projects/maintenance/_show.html.haml +++ b/app/views/projects/maintenance/_show.html.haml @@ -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 = ''.html_safe diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml index 7b27062f782..f02bba387ca 100644 --- a/app/views/projects/mirrors/_mirror_repos.html.haml +++ b/app/views/projects/mirrors/_mirror_repos.html.haml @@ -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') diff --git a/app/views/projects/mirrors/_mirror_repos_list.html.haml b/app/views/projects/mirrors/_mirror_repos_list.html.haml index fbbbb662c94..6e3fd1989b1 100644 --- a/app/views/projects/mirrors/_mirror_repos_list.html.haml +++ b/app/views/projects/mirrors/_mirror_repos_list.html.haml @@ -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 diff --git a/app/views/projects/protected_tags/shared/_index.html.haml b/app/views/projects/protected_tags/shared/_index.html.haml index 5c810b55bec..d0533d59f06 100644 --- a/app/views/projects/protected_tags/shared/_index.html.haml +++ b/app/views/projects/protected_tags/shared/_index.html.haml @@ -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 diff --git a/app/views/projects/protected_tags/shared/_tags_list.html.haml b/app/views/projects/protected_tags/shared/_tags_list.html.haml index 80f0c70955b..03c6e262053 100644 --- a/app/views/projects/protected_tags/shared/_tags_list.html.haml +++ b/app/views/projects/protected_tags/shared/_tags_list.html.haml @@ -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) diff --git a/app/views/protected_branches/shared/_branches_list.html.haml b/app/views/protected_branches/shared/_branches_list.html.haml index 1edec308283..8881076b1c1 100644 --- a/app/views/protected_branches/shared/_branches_list.html.haml +++ b/app/views/protected_branches/shared/_branches_list.html.haml @@ -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 diff --git a/app/views/protected_branches/shared/_index.html.haml b/app/views/protected_branches/shared/_index.html.haml index ce5b58ee189..6ff5e30eefd 100644 --- a/app/views/protected_branches/shared/_index.html.haml +++ b/app/views/protected_branches/shared/_index.html.haml @@ -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' diff --git a/app/views/shared/deploy_keys/_index.html.haml b/app/views/shared/deploy_keys/_index.html.haml index 95c99f20380..d4bf3ece91a 100644 --- a/app/views/shared/deploy_keys/_index.html.haml +++ b/app/views/shared/deploy_keys/_index.html.haml @@ -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 diff --git a/app/views/shared/deploy_tokens/_index.html.haml b/app/views/shared/deploy_tokens/_index.html.haml index 74de71867b8..6349c21c8d2 100644 --- a/app/views/shared/deploy_tokens/_index.html.haml +++ b/app/views/shared/deploy_tokens/_index.html.haml @@ -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 diff --git a/app/views/shared/deploy_tokens/_table.html.haml b/app/views/shared/deploy_tokens/_table.html.haml index 641806d4d82..66c099f2935 100644 --- a/app/views/shared/deploy_tokens/_table.html.haml +++ b/app/views/shared/deploy_tokens/_table.html.haml @@ -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') + } + } diff --git a/config/feature_flags/beta/use_sonnet_35.yml b/config/feature_flags/gitlab_com_derisk/sync_traversal_ids_nowait.yml similarity index 55% rename from config/feature_flags/beta/use_sonnet_35.yml rename to config/feature_flags/gitlab_com_derisk/sync_traversal_ids_nowait.yml index a2ae9c11ca2..abfaf544f4f 100644 --- a/config/feature_flags/beta/use_sonnet_35.yml +++ b/config/feature_flags/gitlab_com_derisk/sync_traversal_ids_nowait.yml @@ -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 diff --git a/db/migrate/20240723102617_remove_old_code_suggestion_events_cron_worker.rb b/db/migrate/20240723102617_remove_old_code_suggestion_events_cron_worker.rb new file mode 100644 index 00000000000..f7e9f5d1884 --- /dev/null +++ b/db/migrate/20240723102617_remove_old_code_suggestion_events_cron_worker.rb @@ -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 diff --git a/db/schema_migrations/20240723102617 b/db/schema_migrations/20240723102617 new file mode 100644 index 00000000000..7225b2ec001 --- /dev/null +++ b/db/schema_migrations/20240723102617 @@ -0,0 +1 @@ +8e40a312b2615cb6677b8ba3c13c7ad339c61bead58ebacf18d985d873ab0a11 \ No newline at end of file diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index 2c2e0c02168..fbbfc3a20b0 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -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). diff --git a/doc/ci/testing/code_quality.md b/doc/ci/testing/code_quality.md index 82d53b4caeb..f9c191c66e1 100644 --- a/doc/ci/testing/code_quality.md +++ b/doc/ci/testing/code_quality.md @@ -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. diff --git a/doc/user/application_security/policies/pipeline_execution_policies.md b/doc/user/application_security/policies/pipeline_execution_policies.md index 43ec0d489b5..174f8f972d1 100644 --- a/doc/user/application_security/policies/pipeline_execution_policies.md +++ b/doc/user/application_security/policies/pipeline_execution_policies.md @@ -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" +``` diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb index d8369c12268..728633c5179 100644 --- a/qa/qa/page/project/settings/deploy_keys.rb +++ b/qa/qa/page/project/settings/deploy_keys.rb @@ -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 diff --git a/spec/components/layouts/crud_component_spec.rb b/spec/components/layouts/crud_component_spec.rb index 9a715ceb038..506635e7fad 100644 --- a/spec/components/layouts/crud_component_spec.rb +++ b/spec/components/layouts/crud_component_spec.rb @@ -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 } diff --git a/spec/components/layouts/settings_block_component_spec.rb b/spec/components/layouts/settings_block_component_spec.rb index ce1c6b3882c..ec092267d0e 100644 --- a/spec/components/layouts/settings_block_component_spec.rb +++ b/spec/components/layouts/settings_block_component_spec.rb @@ -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 diff --git a/spec/components/layouts/settings_section_component_spec.rb b/spec/components/layouts/settings_section_component_spec.rb index 3d6dd3c38b3..984644b9ff3 100644 --- a/spec/components/layouts/settings_section_component_spec.rb +++ b/spec/components/layouts/settings_section_component_spec.rb @@ -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 diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb index aba4a8fccb3..d0372bc7603 100644 --- a/spec/features/projects/settings/repository_settings_spec.rb +++ b/spec/features/projects/settings/repository_settings_spec.rb @@ -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 diff --git a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb index 3d9addfe456..e41f7f769c8 100644 --- a/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb +++ b/spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb @@ -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 diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb index 696a2d11b69..cea0c3a829a 100644 --- a/spec/features/protected_tags_spec.rb +++ b/spec/features/protected_tags_spec.rb @@ -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 diff --git a/spec/frontend/vue_shared/components/settings/settings_section_spec.js b/spec/frontend/vue_shared/components/settings/settings_section_spec.js index b8ef1094e93..e62b2b604a5 100644 --- a/spec/frontend/vue_shared/components/settings/settings_section_spec.js +++ b/spec/frontend/vue_shared/components/settings/settings_section_spec.js @@ -8,8 +8,8 @@ describe('Settings Block', () => { wrapper = mountExtended(SettingsSection, { propsData, slots: { - heading: '
Advanced
', - description: '
', + heading: '
Heading
', + description: '
Description
', default: '
', }, }); @@ -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']); + }); }); diff --git a/spec/lib/banzai/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb index 1fa57bfb974..5b86b098390 100644 --- a/spec/lib/banzai/filter/emoji_filter_spec.rb +++ b/spec/lib/banzai/filter/emoji_filter_spec.rb @@ -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 diff --git a/spec/models/namespace/traversal_hierarchy_spec.rb b/spec/models/namespace/traversal_hierarchy_spec.rb index 8bfcac50b1a..1ffee538ab8 100644 --- a/spec/models/namespace/traversal_hierarchy_spec.rb +++ b/spec/models/namespace/traversal_hierarchy_spec.rb @@ -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') diff --git a/spec/support/shared_examples/row_lock_shared_examples.rb b/spec/support/shared_examples/row_lock_shared_examples.rb index 24fb2d41bdf..3eed6b42653 100644 --- a/spec/support/shared_examples/row_lock_shared_examples.rb +++ b/spec/support/shared_examples/row_lock_shared_examples.rb @@ -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