From ff97077fc8908cc231d967aa7e7133198172b326 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 7 Sep 2021 01:02:14 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../header_search/components/app.vue | 20 +++++ app/assets/javascripts/header_search/index.js | 20 +++++ app/assets/javascripts/main.js | 33 +++++---- app/assets/stylesheets/application_dark.scss | 11 +++ app/assets/stylesheets/pages/search.scss | 10 +++ .../stylesheets/startup/startup-dark.scss | 59 +++++++++++++++ .../stylesheets/startup/startup-general.scss | 35 +++++++++ .../stylesheets/themes/theme_helper.scss | 28 +++++++ .../stylesheets/themes/theme_light.scss | 10 +++ app/graphql/types/ci/stage_type.rb | 14 +--- app/models/ci/pipeline.rb | 6 +- .../preloaders/commit_status_preloader.rb | 29 ++++++++ app/presenters/ci/legacy_stage_presenter.rb | 13 +--- app/presenters/ci/stage_presenter.rb | 18 +---- app/services/ci/drop_pipeline_service.rb | 8 +- app/views/layouts/header/_default.html.haml | 7 +- .../development/new_header_search.yml | 8 ++ doc/ci/variables/index.md | 20 +++++ doc/development/i18n/proofreader.md | 4 +- doc/development/testing_guide/review_apps.md | 2 +- .../migrating_from_gma_to_project_template.md | 23 +++++- lib/gitlab/gon_helper.rb | 1 + locale/gitlab.pot | 3 + scripts/frontend/startup_css/constants.js | 1 + spec/features/global_search_spec.rb | 74 ++++++++++++------- spec/frontend/fixtures/startup_css.rb | 15 ++++ .../header_search/components/app_spec.js | 27 +++++++ .../commit_status_preloader_spec.rb | 41 ++++++++++ spec/spec_helper.rb | 5 ++ 29 files changed, 451 insertions(+), 94 deletions(-) create mode 100644 app/assets/javascripts/header_search/components/app.vue create mode 100644 app/assets/javascripts/header_search/index.js create mode 100644 app/models/preloaders/commit_status_preloader.rb create mode 100644 config/feature_flags/development/new_header_search.yml create mode 100644 spec/frontend/header_search/components/app_spec.js create mode 100644 spec/models/preloaders/commit_status_preloader_spec.rb 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 @@ + + + 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: +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) 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) - - 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 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