Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-09-07 01:02:14 +00:00
parent 183b715218
commit ff97077fc8
29 changed files with 451 additions and 94 deletions

View File

@ -0,0 +1,20 @@
<script>
import { GlSearchBoxByType } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
name: 'HeaderSearchApp',
i18n: {
searchPlaceholder: __('Search or jump to...'),
},
components: {
GlSearchBoxByType,
},
};
</script>
<template>
<section class="header-search">
<gl-search-box-by-type autocomplete="off" :placeholder="$options.i18n.searchPlaceholder" />
</section>
</template>

View File

@ -0,0 +1,20 @@
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import HeaderSearchApp from './components/app.vue';
Vue.use(Translate);
export const initHeaderSearchApp = () => {
const el = document.getElementById('js-header-search');
if (!el) {
return false;
}
return new Vue({
el,
render(createElement) {
return createElement(HeaderSearchApp);
},
});
};

View File

@ -35,6 +35,7 @@ import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import initBroadcastNotifications from './broadcast_notification';
import { initTopNav } from './nav';
import { initHeaderSearchApp } from '~/header_search';
import 'ee_else_ce/main_ee';
import 'jh_else_ce/main_jh';
@ -95,20 +96,24 @@ function deferredInitialisation() {
initDefaultTrackers();
initFeatureHighlight();
const search = document.querySelector('#search');
if (search) {
search.addEventListener(
'focus',
() => {
import(/* webpackChunkName: 'globalSearch' */ './search_autocomplete')
.then(({ default: initSearchAutocomplete }) => {
const searchDropdown = initSearchAutocomplete();
searchDropdown.onSearchInputFocus();
})
.catch(() => {});
},
{ once: true },
);
if (gon.features?.newHeaderSearch) {
initHeaderSearchApp();
} else {
const search = document.querySelector('#search');
if (search) {
search.addEventListener(
'focus',
() => {
import(/* webpackChunkName: 'globalSearch' */ './search_autocomplete')
.then(({ default: initSearchAutocomplete }) => {
const searchDropdown = initSearchAutocomplete();
searchDropdown.onSearchInputFocus();
})
.catch(() => {});
},
{ once: true },
);
}
}
addSelectOnFocusBehaviour('.js-select-on-focus');

View File

@ -44,6 +44,17 @@ body.gl-dark {
}
}
.header-search {
background-color: var(--gray-100) !important;
box-shadow: inset 0 0 0 1px var(--border-color) !important;
&:active,
&:hover {
background-color: var(--gray-100) !important;
box-shadow: inset 0 0 0 1px var(--blue-200) !important;
}
}
.search {
form {
background-color: var(--gray-100);

View File

@ -37,6 +37,16 @@ input[type='checkbox']:hover {
0 0 0 1px lighten($dropdown-input-focus-shadow, 20%);
}
.header-search {
width: 320px;
input,
svg {
transition: border-color ease-in-out $default-transition-duration,
background-color ease-in-out $default-transition-duration;
}
}
.search {
margin: 0 8px;

View File

@ -375,6 +375,38 @@ h1 {
.m-auto {
margin: auto !important;
}
.gl-form-input,
.gl-form-input.form-control {
background-color: #333;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 0.875rem;
line-height: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
height: auto;
color: #fafafa;
box-shadow: inset 0 0 0 1px #868686;
border-style: none;
appearance: none;
-moz-appearance: none;
}
.gl-form-input:disabled,
.gl-form-input:not(.form-control-plaintext):not([type="color"]):read-only,
.gl-form-input.form-control:disabled,
.gl-form-input.form-control:not(.form-control-plaintext):not([type="color"]):read-only {
background-color: #1f1f1f;
color: #868686;
box-shadow: inset 0 0 0 1px #404040;
cursor: not-allowed;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
color: #868686;
}
.gl-button {
display: inline-flex;
}
@ -1410,6 +1442,9 @@ svg.s12 {
svg.s16 {
vertical-align: -3px;
}
.header-search {
width: 320px;
}
.search {
margin: 0 8px;
}
@ -1636,6 +1671,22 @@ body.gl-dark
.notification-dot {
background-color: #fafafa;
}
body.gl-dark .header-search {
background-color: rgba(250, 250, 250, 0.2) !important;
}
body.gl-dark .header-search svg {
color: rgba(250, 250, 250, 0.8) !important;
}
body.gl-dark .header-search input {
background-color: transparent;
color: rgba(250, 250, 250, 0.8);
}
body.gl-dark .header-search input::placeholder {
color: rgba(250, 250, 250, 0.8);
}
body.gl-dark .header-search input:active::placeholder {
color: #fafafa;
}
body.gl-dark .search form {
background-color: rgba(250, 250, 250, 0.2);
}
@ -1669,6 +1720,14 @@ body.gl-dark .navbar-gitlab .navbar-nav li.active > button {
color: var(--gl-text-color);
background-color: var(--gray-200);
}
body.gl-dark .navbar-gitlab .header-search {
background-color: var(--gray-100) !important;
box-shadow: inset 0 0 0 1px var(--border-color) !important;
}
body.gl-dark .navbar-gitlab .header-search:active {
background-color: var(--gray-100) !important;
box-shadow: inset 0 0 0 1px var(--blue-200) !important;
}
body.gl-dark .navbar-gitlab .search form {
background-color: var(--gray-100);
box-shadow: inset 0 0 0 1px var(--border-color);

View File

@ -355,6 +355,38 @@ h1 {
.m-auto {
margin: auto !important;
}
.gl-form-input,
.gl-form-input.form-control {
background-color: #fff;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 0.875rem;
line-height: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
height: auto;
color: #303030;
box-shadow: inset 0 0 0 1px #868686;
border-style: none;
appearance: none;
-moz-appearance: none;
}
.gl-form-input:disabled,
.gl-form-input:not(.form-control-plaintext):not([type="color"]):read-only,
.gl-form-input.form-control:disabled,
.gl-form-input.form-control:not(.form-control-plaintext):not([type="color"]):read-only {
background-color: #fafafa;
color: #868686;
box-shadow: inset 0 0 0 1px #dbdbdb;
cursor: not-allowed;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
color: #868686;
}
.gl-button {
display: inline-flex;
}
@ -1390,6 +1422,9 @@ svg.s12 {
svg.s16 {
vertical-align: -3px;
}
.header-search {
width: 320px;
}
.search {
margin: 0 8px;
}

View File

@ -140,6 +140,34 @@
}
}
.header-search {
background-color: rgba($search-and-nav-links, 0.2) !important;
&:hover {
background-color: rgba($search-and-nav-links, 0.3) !important;
}
svg {
color: rgba($search-and-nav-links, 0.8) !important;
}
input {
background-color: transparent;
color: rgba($search-and-nav-links, 0.8);
&::placeholder {
color: rgba($search-and-nav-links, 0.8);
}
&:focus,
&:active {
&::placeholder {
color: $search-and-nav-links;
}
}
}
}
.search {
form {
background-color: rgba($search-and-nav-links, 0.2);

View File

@ -45,6 +45,16 @@ body {
}
}
.header-search {
background-color: $white !important;
box-shadow: inset 0 0 0 1px $border-color !important;
&:hover {
background-color: $white !important;
box-shadow: inset 0 0 0 1px $blue-200 !important;
}
}
.search {
form {
background-color: $white;

View File

@ -55,18 +55,10 @@ module Types
def jobs_for_pipeline(pipeline, stage_ids, include_needs)
jobs = pipeline.statuses.latest.where(stage_id: stage_ids)
common_relations = [:project]
common_relations << :needs if include_needs
preloaded_relations = [:project, :metadata, :job_artifacts, :downstream_pipeline]
preloaded_relations << :needs if include_needs
preloaders = {
::Ci::Build => [:metadata, :job_artifacts],
::Ci::Bridge => [:metadata, :downstream_pipeline],
::GenericCommitStatus => []
}
preloaders.each do |klass, relations|
ActiveRecord::Associations::Preloader.new.preload(jobs.select { |job| job.is_a?(klass) }, relations + common_relations)
end
Preloaders::CommitStatusPreloader.new(jobs).execute(preloaded_relations)
jobs.group_by(&:stage_id)
end

View File

@ -589,13 +589,11 @@ module Ci
end
def cancel_running(retries: 1)
commit_status_relations = [:project, :pipeline]
ci_build_relations = [:deployment, :taggings]
preloaded_relations = [:project, :pipeline, :deployment, :taggings]
retry_lock(cancelable_statuses, retries, name: 'ci_pipeline_cancel_running') do |cancelables|
cancelables.find_in_batches do |batch|
ActiveRecord::Associations::Preloader.new.preload(batch, commit_status_relations)
ActiveRecord::Associations::Preloader.new.preload(batch.select { |job| job.is_a?(Ci::Build) }, ci_build_relations)
Preloaders::CommitStatusPreloader.new(batch).execute(preloaded_relations)
batch.each do |job|
yield(job) if block_given?

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
module Preloaders
class CommitStatusPreloader
CLASSES = [::Ci::Build, ::Ci::Bridge, ::GenericCommitStatus].freeze
def initialize(statuses)
@statuses = statuses
end
def execute(relations)
preloader = ActiveRecord::Associations::Preloader.new
CLASSES.each do |klass|
preloader.preload(objects(klass), associations(klass, relations))
end
end
private
def objects(klass)
@statuses.select { |job| job.is_a?(klass) }
end
def associations(klass, relations)
klass.reflections.keys.map(&:to_sym) & relations.map(&:to_sym)
end
end
end

View File

@ -15,18 +15,9 @@ module Ci
private
def preload_statuses(statuses)
loaded_statuses = statuses.load
statuses.tap do |statuses|
# rubocop: disable CodeReuse/ActiveRecord
ActiveRecord::Associations::Preloader.new.preload(preloadable_statuses(loaded_statuses), %w[tags job_artifacts_archive metadata])
# rubocop: enable CodeReuse/ActiveRecord
end
end
Preloaders::CommitStatusPreloader.new(statuses).execute(Ci::StagePresenter::PRELOADED_RELATIONS)
def preloadable_statuses(statuses)
statuses.reject do |status|
status.instance_of?(::GenericCommitStatus) || status.instance_of?(::Ci::Bridge)
end
statuses
end
end
end

View File

@ -4,6 +4,8 @@ module Ci
class StagePresenter < Gitlab::View::Presenter::Delegated
presents :stage
PRELOADED_RELATIONS = [:pipeline, :metadata, :tags, :job_artifacts_archive, :downstream_pipeline].freeze
def latest_ordered_statuses
preload_statuses(stage.statuses.latest_ordered)
end
@ -15,21 +17,7 @@ module Ci
private
def preload_statuses(statuses)
common_relations = [:pipeline]
preloaders = {
::Ci::Build => [:metadata, :tags, :job_artifacts_archive],
::Ci::Bridge => [:metadata, :downstream_pipeline],
::GenericCommitStatus => []
}
# rubocop: disable CodeReuse/ActiveRecord
preloaders.each do |klass, relations|
ActiveRecord::Associations::Preloader
.new
.preload(statuses.select { |job| job.is_a?(klass) }, relations + common_relations)
end
# rubocop: enable CodeReuse/ActiveRecord
Preloaders::CommitStatusPreloader.new(statuses).execute(PRELOADED_RELATIONS)
statuses
end

View File

@ -2,8 +2,7 @@
module Ci
class DropPipelineService
PRELOADED_COMMIT_STATUS_RELATIONS = [:project, :pipeline].freeze
PRELOADED_CI_BUILD_RELATIONS = [:metadata, :deployment, :taggings].freeze
PRELOADED_RELATIONS = [:project, :pipeline, :metadata, :deployment, :taggings].freeze
# execute service asynchronously for each cancelable pipeline
def execute_async_for_all(pipelines, failure_reason, context_user)
@ -30,11 +29,8 @@ module Ci
private
# rubocop: disable CodeReuse/ActiveRecord
def preload_associations_for_drop(commit_status_batch)
ActiveRecord::Associations::Preloader.new.preload(commit_status_batch, PRELOADED_COMMIT_STATUS_RELATIONS)
ActiveRecord::Associations::Preloader.new.preload(commit_status_batch.select { |job| job.is_a?(Ci::Build) }, PRELOADED_CI_BUILD_RELATIONS)
Preloaders::CommitStatusPreloader.new(commit_status_batch).execute(PRELOADED_RELATIONS)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end

View File

@ -29,7 +29,12 @@
- if top_nav_show_search
- search_menu_item = top_nav_search_menu_item_attrs
%li.nav-item.d-none.d-lg-block.m-auto
= render 'layouts/search' unless current_controller?(:search)
- unless current_controller?(:search)
- if Feature.enabled?(:new_header_search)
#js-header-search.header-search{ }
%input{ type: "text", placeholder: _('Search or jump to...'), class: 'form-control gl-form-input' }
- else
= render 'layouts/search'
%li.nav-item{ class: 'd-none d-sm-inline-block d-lg-none' }
= link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon(search_menu_item.fetch(:icon))

View File

@ -0,0 +1,8 @@
---
name: new_header_search
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68681
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339348
milestone: '14.3'
type: development
group: group::global search
default_enabled: false

View File

@ -304,6 +304,26 @@ echo "$KUBE_CA_PEM" > "$(pwd)/kube.ca.pem"
kubectl config set-cluster e2e --server="$KUBE_URL" --certificate-authority="$(pwd)/kube.ca.pem"
```
#### Store multiple values in one variable
It is not possible to create a CI/CD variable that is an array of values, but you
can use shell scripting techniques for similar behavior.
For example, you can store multiple variables separated by a space in a variable,
then loop through the values with a script:
```yaml
job1:
variables:
FOLDERS: src test docs
script:
- |
for FOLDER in $FOLDERS
do
echo "The path is root/${FOLDER}"
done
```
### Mask a CI/CD variable
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/13784) in GitLab 11.10

View File

@ -99,8 +99,8 @@ are very appreciative of the work done by translators and proofreaders!
- André Gama - [GitLab](https://gitlab.com/andregamma), [Crowdin](https://crowdin.com/profile/ToeOficial)
- Eduardo Addad de Oliveira - [GitLab](https://gitlab.com/eduardoaddad), [Crowdin](https://crowdin.com/profile/eduardoaddad)
- Romanian
- Mircea Pop - [GitLab](https://gitlab.com/eeex)[Crowdin](https://crowdin.com/profile/eex)
- Rareș Pița - [GitLab](https://gitlab.com/dlphin)[Crowdin](https://crowdin.com/profile/dlphin)
- Mircea Pop - [GitLab](https://gitlab.com/eeex), [Crowdin](https://crowdin.com/profile/eex)
- Rareș Pița - [GitLab](https://gitlab.com/dlphin), [Crowdin](https://crowdin.com/profile/dlphin)
- Russian
- Nikita Grylov - [GitLab](https://gitlab.com/nixel2007), [Crowdin](https://crowdin.com/profile/nixel2007)
- Alexy Lustin - [GitLab](https://gitlab.com/allustin), [Crowdin](https://crowdin.com/profile/lustin)

View File

@ -175,7 +175,7 @@ For GitLab Team Members only. If you want to sign in to the review app, review
the GitLab handbook information for the [shared 1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams).
- The default username is `root`.
- The password can be found in the 1Password secure note named `gitlab-{ce,ee} Review App's root password`.
- The password can be found in the 1Password login item named `GitLab EE Review App`.
### Enable a feature flag for my Review App

View File

@ -6,11 +6,21 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Migrating from GitLab Managed Apps to a management project template
The [GitLab Managed Apps](applications.md) are deprecated in GitLab 14.0. To migrate to the new way of managing them:
The [GitLab Managed Apps](applications.md) deprecated in GitLab 14.0.
To manage your apps through a cluster management project, you need a [GitLab Runner](../../ci/runners/index.md) available.
Then, follow the steps below. You can also watch
some recorded videos with [live examples](#live-examples).
1. Read how the [management project template](management_project_template.md) works, and
create a new project based on the "GitLab Cluster Management" template.
1. Create a new project as explained in the [management project template](management_project_template.md).
1. Familiarize yourself with the [management project template](management_project_template.md).
1. Create a [new project](../project/working_with_projects.md#create-a-project), either:
- From a template, selecting the **GitLab Cluster Management** project template.
- Importing the project from the URL `https://gitlab.com/gitlab-org/project-templates/cluster-management.git`. This
is useful if you are using GitLab Self-Managed and you want to use the latest version of the template.
This is your cluster management project.
If you are using a self-managed GitLab instance older than the latest one, import the cluster management project via URL from `https://gitlab.com/gitlab-org/project-templates/cluster-management.git`.
1. Go to the project associated with your cluster.
1. In your cluster's configuration page [set the cluster management project](management_project.md#selecting-a-cluster-management-project) that you just created.
1. Detect apps deployed through Helm v2 releases by using the pre-configured [`.gitlab-ci.yml`](management_project_template.md#the-gitlab-ciyml-file) file:
- In case you had overwritten the default GitLab Managed Apps namespace, edit `.gitlab-ci.yml`,
and make sure the script is receiving the correct namespace as an argument:
@ -93,3 +103,8 @@ The [GitLab Managed Apps](applications.md) are deprecated in GitLab 14.0. To mig
After getting a successful pipeline, repeat these steps for any other deployed apps
you want to manage with the Cluster Management Project.
## Live examples
- [Migrating from scratch using a brand new cluster management project](https://youtu.be/jCUFGWT0jS0). Also covers Helm v2 apps migration.
- [Migrating from an existing GitLab managed apps CI/CD project](https://youtu.be/U2lbBGZjZmc)

View File

@ -54,6 +54,7 @@ module Gitlab
push_frontend_feature_flag(:usage_data_api, type: :ops, default_enabled: :yaml)
push_frontend_feature_flag(:security_auto_fix, default_enabled: false)
push_frontend_feature_flag(:improved_emoji_picker, default_enabled: :yaml)
push_frontend_feature_flag(:new_header_search, default_enabled: :yaml)
end
# Exposes the state of a feature flag to the frontend code.

View File

@ -29359,6 +29359,9 @@ msgstr ""
msgid "Search or filter results…"
msgstr ""
msgid "Search or jump to..."
msgstr ""
msgid "Search project"
msgstr ""

View File

@ -50,6 +50,7 @@ const createMainOutput = ({ outFile, cssKeys, type }) => ({
htmlPaths: [
path.join(FIXTURES_ROOT, `startup_css/project-${type}.html`),
path.join(FIXTURES_ROOT, `startup_css/project-${type}-signed-out.html`),
path.join(FIXTURES_ROOT, `startup_css/project-${type}-search-ff-on.html`),
],
cssKeys,
purgeOptions: {

View File

@ -11,40 +11,64 @@ RSpec.describe 'Global search' do
before do
project.add_maintainer(user)
sign_in(user)
visit dashboard_projects_path
end
it 'increases usage ping searches counter' do
expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:navbar_searches)
expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:all_searches)
submit_search('foobar')
end
describe 'I search through the issues and I see pagination' do
describe 'when new_header_search feature is disabled' do
before do
allow_next(SearchService).to receive(:per_page).and_return(1)
create_list(:issue, 2, project: project, title: 'initial')
# TODO: Remove this along with feature flag #339348
stub_feature_flags(new_header_search: false)
visit dashboard_projects_path
end
it "has a pagination" do
submit_search('initial')
select_search_scope('Issues')
it 'increases usage ping searches counter' do
expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:navbar_searches)
expect(Gitlab::UsageDataCounters::SearchCounter).to receive(:count).with(:all_searches)
expect(page).to have_selector('.gl-pagination .next')
submit_search('foobar')
end
describe 'I search through the issues and I see pagination' do
before do
allow_next(SearchService).to receive(:per_page).and_return(1)
create_list(:issue, 2, project: project, title: 'initial')
end
it "has a pagination" do
submit_search('initial')
select_search_scope('Issues')
expect(page).to have_selector('.gl-pagination .next')
end
end
it 'closes the dropdown on blur', :js do
find('#search').click
fill_in 'search', with: "a"
expect(page).to have_selector("div[data-testid='dashboard-search-options'].show")
find('#search').send_keys(:backspace)
find('body').click
expect(page).to have_no_selector("div[data-testid='dashboard-search-options'].show")
end
it 'renders legacy search bar' do
expect(page).to have_selector('.search-form')
expect(page).to have_no_selector('#js-header-search')
end
end
it 'closes the dropdown on blur', :js do
find('#search').click
fill_in 'search', with: "a"
describe 'when new_header_search feature is enabled' do
before do
# TODO: Remove this along with feature flag #339348
stub_feature_flags(new_header_search: true)
visit dashboard_projects_path
end
expect(page).to have_selector("div[data-testid='dashboard-search-options'].show")
find('#search').send_keys(:backspace)
find('body').click
expect(page).to have_no_selector("div[data-testid='dashboard-search-options'].show")
it 'renders updated search bar' do
expect(page).to have_no_selector('.search-form')
expect(page).to have_selector('#js-header-search')
end
end
end

View File

@ -40,6 +40,21 @@ RSpec.describe 'Startup CSS fixtures', type: :controller do
expect(response).to be_successful
end
# This Feature Flag is off by default
# This ensures that the correct css is generated
# When the feature flag is off, the general startup will capture it
# This will be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/339348
it "startup_css/project-#{type}-search-ff-on.html" do
stub_feature_flags(new_header_search: true)
get :show, params: {
namespace_id: project.namespace.to_param,
id: project
}
expect(response).to be_successful
end
end
describe ProjectsController, '(Startup CSS fixtures)', type: :controller do

View File

@ -0,0 +1,27 @@
import { GlSearchBoxByType } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import HeaderSearchApp from '~/header_search/components/app.vue';
describe('HeaderSearchApp', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(HeaderSearchApp);
};
afterEach(() => {
wrapper.destroy();
});
const findHeaderSearchInput = () => wrapper.findComponent(GlSearchBoxByType);
describe('template', () => {
beforeEach(() => {
createComponent();
});
it('renders Header Search Input always', () => {
expect(findHeaderSearchInput().exists()).toBe(true);
});
});
});

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Preloaders::CommitStatusPreloader do
let_it_be(:pipeline) { create(:ci_pipeline) }
let_it_be(:build1) { create(:ci_build, :tags, pipeline: pipeline) }
let_it_be(:build2) { create(:ci_build, :tags, pipeline: pipeline) }
let_it_be(:bridge1) { create(:ci_bridge, pipeline: pipeline) }
let_it_be(:bridge2) { create(:ci_bridge, pipeline: pipeline) }
let_it_be(:generic_commit_status1) { create(:generic_commit_status, pipeline: pipeline) }
let_it_be(:generic_commit_status2) { create(:generic_commit_status, pipeline: pipeline) }
describe '#execute' do
let(:relations) { %i[pipeline metadata tags job_artifacts_archive downstream_pipeline] }
let(:statuses) { CommitStatus.where(commit_id: pipeline.id).all }
subject(:execute) { described_class.new(statuses).execute(relations) }
it 'prevents N+1 for specified relations', :use_sql_query_cache do
execute
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
call_each_relation(statuses.sample(3))
end
expect do
call_each_relation(statuses)
end.to issue_same_number_of_queries_as(control_count)
end
private
def call_each_relation(statuses)
statuses.each do |status|
relations.each { |relation| status.public_send(relation) if status.respond_to?(relation) }
end
end
end
end

View File

@ -303,6 +303,11 @@ RSpec.configure do |config|
# For more information check https://gitlab.com/gitlab-com/gl-infra/production/-/issues/4321
stub_feature_flags(block_issue_repositioning: false)
# Disable the refactored top nav search until there is functionality
# Can be removed once all existing functionality has been replicated
# For more information check https://gitlab.com/gitlab-org/gitlab/-/issues/339348
stub_feature_flags(new_header_search: false)
allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged)
else
unstub_all_feature_flags