Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/header_search/components/app.vue20
-rw-r--r--app/assets/javascripts/header_search/index.js20
-rw-r--r--app/assets/javascripts/main.js33
-rw-r--r--app/assets/stylesheets/application_dark.scss11
-rw-r--r--app/assets/stylesheets/pages/search.scss10
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss59
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss35
-rw-r--r--app/assets/stylesheets/themes/theme_helper.scss28
-rw-r--r--app/assets/stylesheets/themes/theme_light.scss10
-rw-r--r--app/graphql/types/ci/stage_type.rb14
-rw-r--r--app/models/ci/pipeline.rb6
-rw-r--r--app/models/preloaders/commit_status_preloader.rb29
-rw-r--r--app/presenters/ci/legacy_stage_presenter.rb13
-rw-r--r--app/presenters/ci/stage_presenter.rb18
-rw-r--r--app/services/ci/drop_pipeline_service.rb8
-rw-r--r--app/views/layouts/header/_default.html.haml7
-rw-r--r--config/feature_flags/development/new_header_search.yml8
-rw-r--r--doc/ci/variables/index.md20
-rw-r--r--doc/development/i18n/proofreader.md4
-rw-r--r--doc/development/testing_guide/review_apps.md2
-rw-r--r--doc/user/clusters/migrating_from_gma_to_project_template.md25
-rw-r--r--lib/gitlab/gon_helper.rb1
-rw-r--r--locale/gitlab.pot3
-rw-r--r--scripts/frontend/startup_css/constants.js1
-rw-r--r--spec/features/global_search_spec.rb70
-rw-r--r--spec/frontend/fixtures/startup_css.rb15
-rw-r--r--spec/frontend/header_search/components/app_spec.js27
-rw-r--r--spec/models/preloaders/commit_status_preloader_spec.rb41
-rw-r--r--spec/spec_helper.rb5
29 files changed, 450 insertions, 93 deletions
diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue
new file mode 100644
index 00000000000..0c4df9cf522
--- /dev/null
+++ b/app/assets/javascripts/header_search/components/app.vue
@@ -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>
diff --git a/app/assets/javascripts/header_search/index.js b/app/assets/javascripts/header_search/index.js
new file mode 100644
index 00000000000..fa1ac71655c
--- /dev/null
+++ b/app/assets/javascripts/header_search/index.js
@@ -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);
+ },
+ });
+};
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index abd13a30156..15c483485f1 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -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');
diff --git a/app/assets/stylesheets/application_dark.scss b/app/assets/stylesheets/application_dark.scss
index 7d6ccc40278..dae0cd72a8f 100644
--- a/app/assets/stylesheets/application_dark.scss
+++ b/app/assets/stylesheets/application_dark.scss
@@ -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);
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 8f5de73365b..889043b320f 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -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;
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 4aa16933b65..feb7469fb62 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -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);
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 0b2d34b6f5d..2c79b819899 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -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;
}
diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss
index e9915ec39ef..a9e8b238d78 100644
--- a/app/assets/stylesheets/themes/theme_helper.scss
+++ b/app/assets/stylesheets/themes/theme_helper.scss
@@ -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);
diff --git a/app/assets/stylesheets/themes/theme_light.scss b/app/assets/stylesheets/themes/theme_light.scss
index b41377475c5..4c3bc1b2298 100644
--- a/app/assets/stylesheets/themes/theme_light.scss
+++ b/app/assets/stylesheets/themes/theme_light.scss
@@ -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;
diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb
index 57b64cb7ba5..00fbb01b730 100644
--- a/app/graphql/types/ci/stage_type.rb
+++ b/app/graphql/types/ci/stage_type.rb
@@ -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
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index eb99811ca4f..4f718814354 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -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?
diff --git a/app/models/preloaders/commit_status_preloader.rb b/app/models/preloaders/commit_status_preloader.rb
new file mode 100644
index 00000000000..535dd24ba6b
--- /dev/null
+++ b/app/models/preloaders/commit_status_preloader.rb
@@ -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
diff --git a/app/presenters/ci/legacy_stage_presenter.rb b/app/presenters/ci/legacy_stage_presenter.rb
index 56e268cff9f..d5c21baba28 100644
--- a/app/presenters/ci/legacy_stage_presenter.rb
+++ b/app/presenters/ci/legacy_stage_presenter.rb
@@ -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
diff --git a/app/presenters/ci/stage_presenter.rb b/app/presenters/ci/stage_presenter.rb
index 2d5ee3d2f25..21bda86cded 100644
--- a/app/presenters/ci/stage_presenter.rb
+++ b/app/presenters/ci/stage_presenter.rb
@@ -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
diff --git a/app/services/ci/drop_pipeline_service.rb b/app/services/ci/drop_pipeline_service.rb
index 16d3abcbfa0..5772ab8f29c 100644
--- a/app/services/ci/drop_pipeline_service.rb
+++ b/app/services/ci/drop_pipeline_service.rb
@@ -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
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 2f6287bdfb3..fef381d3414 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -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))
diff --git a/config/feature_flags/development/new_header_search.yml b/config/feature_flags/development/new_header_search.yml
new file mode 100644
index 00000000000..086aee4c4b0
--- /dev/null
+++ b/config/feature_flags/development/new_header_search.yml
@@ -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
diff --git a/doc/ci/variables/index.md b/doc/ci/variables/index.md
index 7b909cf15f8..dde3981d85e 100644
--- a/doc/ci/variables/index.md
+++ b/doc/ci/variables/index.md
@@ -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
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index ac8af0f9420..76ab00eebfb 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -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)
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index cf757aad870..72d63fd8194 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -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
diff --git a/doc/user/clusters/migrating_from_gma_to_project_template.md b/doc/user/clusters/migrating_from_gma_to_project_template.md
index dc16cf5cc45..5144ddc5e06 100644
--- a/doc/user/clusters/migrating_from_gma_to_project_template.md
+++ b/doc/user/clusters/migrating_from_gma_to_project_template.md
@@ -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:
-
-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).
+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. 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)
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 861ca1e757c..258c13894fb 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -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.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index cf3cf30b75e..f3f8aa49981 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -29359,6 +29359,9 @@ msgstr ""
msgid "Search or filter results…"
msgstr ""
+msgid "Search or jump to..."
+msgstr ""
+
msgid "Search project"
msgstr ""
diff --git a/scripts/frontend/startup_css/constants.js b/scripts/frontend/startup_css/constants.js
index 8f183e63659..83f43143e1b 100644
--- a/scripts/frontend/startup_css/constants.js
+++ b/scripts/frontend/startup_css/constants.js
@@ -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: {
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index 19fb8e5f52c..a380edff3a4 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -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)
+ describe 'when new_header_search feature is disabled' do
+ before do
+ # TODO: Remove this along with feature flag #339348
+ stub_feature_flags(new_header_search: false)
+ visit dashboard_projects_path
+ end
- submit_search('foobar')
- 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)
- 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')
+ submit_search('foobar')
end
- it "has a pagination" do
- submit_search('initial')
- select_search_scope('Issues')
+ 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')
+ expect(page).to have_selector('.gl-pagination .next')
+ end
end
- end
- it 'closes the dropdown on blur', :js do
- find('#search').click
- fill_in 'search', with: "a"
+ 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")
- expect(page).to have_selector("div[data-testid='dashboard-search-options'].show")
+ find('#search').send_keys(:backspace)
+ find('body').click
- 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
- expect(page).to have_no_selector("div[data-testid='dashboard-search-options'].show")
+ 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
+
+ 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
diff --git a/spec/frontend/fixtures/startup_css.rb b/spec/frontend/fixtures/startup_css.rb
index be2ead756cf..1bd99f5cd7f 100644
--- a/spec/frontend/fixtures/startup_css.rb
+++ b/spec/frontend/fixtures/startup_css.rb
@@ -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
diff --git a/spec/frontend/header_search/components/app_spec.js b/spec/frontend/header_search/components/app_spec.js
new file mode 100644
index 00000000000..200c823e592
--- /dev/null
+++ b/spec/frontend/header_search/components/app_spec.js
@@ -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);
+ });
+ });
+});
diff --git a/spec/models/preloaders/commit_status_preloader_spec.rb b/spec/models/preloaders/commit_status_preloader_spec.rb
new file mode 100644
index 00000000000..85ea784335c
--- /dev/null
+++ b/spec/models/preloaders/commit_status_preloader_spec.rb
@@ -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
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index e9140c32968..6ccfabca101 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -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