From 8d9c82762d6c4f10f4d3eba5d484179f98c284b0 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 3 Mar 2021 00:10:50 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- app/controllers/concerns/boards_responses.rb | 4 +- .../concerns/multiple_boards_actions.rb | 2 +- app/controllers/groups/boards_controller.rb | 2 +- app/controllers/projects/boards_controller.rb | 2 +- app/graphql/mutations/boards/create.rb | 2 +- app/graphql/mutations/boards/destroy.rb | 2 +- .../boards/issues/issue_move_list.rb | 2 +- app/graphql/mutations/boards/lists/create.rb | 2 +- app/graphql/mutations/boards/lists/destroy.rb | 2 +- app/graphql/mutations/boards/lists/update.rb | 2 +- app/graphql/mutations/boards/update.rb | 2 +- app/graphql/resolvers/board_lists_resolver.rb | 2 +- app/graphql/types/access_level_enum.rb | 13 +- app/graphql/types/board_type.rb | 2 +- app/mailers/emails/merge_requests.rb | 8 ++ app/models/ci/runner.rb | 30 ++++- app/policies/concerns/readonly_abilities.rb | 2 +- app/policies/group_policy.rb | 8 +- app/policies/project_policy.rb | 20 +-- app/services/boards/lists/update_service.rb | 4 +- app/services/ci/update_build_queue_service.rb | 2 + app/services/merge_requests/update_service.rb | 9 ++ app/services/notification_service.rb | 14 +++ app/views/shared/boards/_show.html.haml | 2 +- app/views/shared/boards/_switcher.html.haml | 2 +- .../shared/issuable/_search_bar.html.haml | 4 +- ...hen-merge-request-draft-status-changes.yml | 5 + ...duce-queries-when-ticking-runner-queue.yml | 5 + changelogs/unreleased/preload-runner-tags.yml | 5 + .../development/ci_preload_runner_tags.yml | 8 ++ ...duce_queries_when_ticking_runner_queue.yml | 8 ++ .../geo/replication/datatypes.md | 2 +- doc/administration/instance_limits.md | 2 +- doc/administration/logs.md | 2 +- doc/api/graphql/reference/index.md | 13 +- doc/api/search.md | 2 +- doc/development/elasticsearch.md | 2 +- doc/integration/README.md | 3 +- doc/integration/elasticsearch.md | 5 +- doc/subscriptions/bronze_starter.md | 5 +- doc/topics/git/cherry_picking.md | 53 ++++++++ .../training/topics/cherry_picking.md | 36 +----- doc/university/training/topics/tags.md | 2 +- doc/user/clusters/applications.md | 2 +- doc/user/index.md | 2 +- .../merge_requests/merge_request_approvals.md | 3 +- doc/user/search/advanced_global_search.md | 70 +---------- doc/user/search/advanced_search.md | 115 ++++++++++++++++++ doc/user/search/advanced_search_syntax.md | 90 +------------- .../search/img/advanced_global_search.png | Bin 15017 -> 0 bytes .../search/img/advanced_search_v13.10.png | Bin 0 -> 46767 bytes doc/user/search/index.md | 8 +- lib/api/boards.rb | 20 +-- lib/api/group_boards.rb | 16 +-- .../groups/boards_controller_spec.rb | 6 +- .../projects/boards_controller_spec.rb | 8 +- spec/graphql/mutations/boards/update_spec.rb | 2 +- spec/graphql/types/access_level_enum_spec.rb | 2 +- spec/graphql/types/board_type_spec.rb | 2 +- spec/models/ci/runner_spec.rb | 43 +++++-- spec/models/user_spec.rb | 2 +- spec/policies/project_policy_spec.rb | 4 +- .../ci/update_build_queue_service_spec.rb | 57 +++++++++ .../merge_requests/update_service_spec.rb | 42 +++++++ spec/services/notification_service_spec.rb | 36 ++++++ .../policies/group_policy_shared_context.rb | 4 +- .../policies/project_policy_shared_context.rb | 6 +- ...nd_project_boards_query_shared_examples.rb | 2 +- 68 files changed, 538 insertions(+), 308 deletions(-) create mode 100644 changelogs/unreleased/15332-notify-when-merge-request-draft-status-changes.yml create mode 100644 changelogs/unreleased/fp-reduce-queries-when-ticking-runner-queue.yml create mode 100644 changelogs/unreleased/preload-runner-tags.yml create mode 100644 config/feature_flags/development/ci_preload_runner_tags.yml create mode 100644 config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml create mode 100644 doc/topics/git/cherry_picking.md create mode 100644 doc/user/search/advanced_search.md delete mode 100644 doc/user/search/img/advanced_global_search.png create mode 100644 doc/user/search/img/advanced_search_v13.10.png diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb index 6e6686f225c..7307b7b4f8f 100644 --- a/app/controllers/concerns/boards_responses.rb +++ b/app/controllers/concerns/boards_responses.rb @@ -35,7 +35,7 @@ module BoardsResponses end def authorize_read_list - authorize_action_for!(board, :read_list) + authorize_action_for!(board, :read_issue_board_list) end def authorize_read_issue @@ -54,7 +54,7 @@ module BoardsResponses end def authorize_admin_list - authorize_action_for!(board, :admin_list) + authorize_action_for!(board, :admin_issue_board_list) end def authorize_action_for!(resource, ability) diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb index 5206f5759d8..85bb73463db 100644 --- a/app/controllers/concerns/multiple_boards_actions.rb +++ b/app/controllers/concerns/multiple_boards_actions.rb @@ -80,7 +80,7 @@ module MultipleBoardsActions end def authorize_admin_board! - return render_404 unless can?(current_user, :admin_board, parent) + return render_404 unless can?(current_user, :admin_issue_board, parent) end def serializer diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index fa109021b7d..379fb7bb38b 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -44,6 +44,6 @@ class Groups::BoardsController < Groups::ApplicationController end def authorize_read_board! - access_denied! unless can?(current_user, :read_board, group) + access_denied! unless can?(current_user, :read_issue_board, group) end end diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index d2e5d319f96..f9d84a9dcf7 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -45,6 +45,6 @@ class Projects::BoardsController < Projects::ApplicationController end def authorize_read_board! - access_denied! unless can?(current_user, :read_board, project) + access_denied! unless can?(current_user, :read_issue_board, project) end end diff --git a/app/graphql/mutations/boards/create.rb b/app/graphql/mutations/boards/create.rb index 92bce557446..fe324f848e7 100644 --- a/app/graphql/mutations/boards/create.rb +++ b/app/graphql/mutations/boards/create.rb @@ -14,7 +14,7 @@ module Mutations null: true, description: 'The board after mutation.' - authorize :admin_board + authorize :admin_issue_board def resolve(args) board_parent = authorized_resource_parent_find!(args) diff --git a/app/graphql/mutations/boards/destroy.rb b/app/graphql/mutations/boards/destroy.rb index 8ec13b885d5..4a0068edee2 100644 --- a/app/graphql/mutations/boards/destroy.rb +++ b/app/graphql/mutations/boards/destroy.rb @@ -14,7 +14,7 @@ module Mutations required: true, description: 'The global ID of the board to destroy.' - authorize :admin_board + authorize :admin_issue_board def resolve(id:) board = authorized_find!(id: id) diff --git a/app/graphql/mutations/boards/issues/issue_move_list.rb b/app/graphql/mutations/boards/issues/issue_move_list.rb index 91dfd9fc3e9..6df4250fc67 100644 --- a/app/graphql/mutations/boards/issues/issue_move_list.rb +++ b/app/graphql/mutations/boards/issues/issue_move_list.rb @@ -83,7 +83,7 @@ module Mutations end def authorize_board!(board) - return if Ability.allowed?(current_user, :read_board, board.resource_parent) + return if Ability.allowed?(current_user, :read_issue_board, board.resource_parent) raise_resource_not_available_error! end diff --git a/app/graphql/mutations/boards/lists/create.rb b/app/graphql/mutations/boards/lists/create.rb index f3aae9ac9c8..673fa95fc56 100644 --- a/app/graphql/mutations/boards/lists/create.rb +++ b/app/graphql/mutations/boards/lists/create.rb @@ -15,7 +15,7 @@ module Mutations null: true, description: 'Issue list in the issue board.' - authorize :admin_list + authorize :admin_issue_board_list private diff --git a/app/graphql/mutations/boards/lists/destroy.rb b/app/graphql/mutations/boards/lists/destroy.rb index 61ffae7c047..a50b5f73455 100644 --- a/app/graphql/mutations/boards/lists/destroy.rb +++ b/app/graphql/mutations/boards/lists/destroy.rb @@ -33,7 +33,7 @@ module Mutations def can_admin_list?(list) return false unless list.present? - Ability.allowed?(current_user, :admin_list, list.board) + Ability.allowed?(current_user, :admin_issue_board_list, list.board) end end end diff --git a/app/graphql/mutations/boards/lists/update.rb b/app/graphql/mutations/boards/lists/update.rb index 0a025a21353..504082ec22c 100644 --- a/app/graphql/mutations/boards/lists/update.rb +++ b/app/graphql/mutations/boards/lists/update.rb @@ -44,7 +44,7 @@ module Mutations def can_read_list?(list) return false unless list.present? - Ability.allowed?(current_user, :read_list, list.board) + Ability.allowed?(current_user, :read_issue_board_list, list.board) end end end diff --git a/app/graphql/mutations/boards/update.rb b/app/graphql/mutations/boards/update.rb index b4f8179829e..628b3a3fadb 100644 --- a/app/graphql/mutations/boards/update.rb +++ b/app/graphql/mutations/boards/update.rb @@ -17,7 +17,7 @@ module Mutations null: true, description: 'The board after mutation.' - authorize :admin_board + authorize :admin_issue_board def resolve(id:, **args) board = authorized_find!(id: id) diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb index a97ac3220d5..e66f7b97b40 100644 --- a/app/graphql/resolvers/board_lists_resolver.rb +++ b/app/graphql/resolvers/board_lists_resolver.rb @@ -9,7 +9,7 @@ module Resolvers type Types::BoardListType, null: true extras [:lookahead] - authorize :read_list + authorize :read_issue_board_list argument :id, Types::GlobalIDType[List], required: false, diff --git a/app/graphql/types/access_level_enum.rb b/app/graphql/types/access_level_enum.rb index 6754d3d28ce..b7eb35ddfc9 100644 --- a/app/graphql/types/access_level_enum.rb +++ b/app/graphql/types/access_level_enum.rb @@ -5,11 +5,12 @@ module Types graphql_name 'AccessLevelEnum' description 'Access level to a resource' - value 'NO_ACCESS', value: Gitlab::Access::NO_ACCESS - value 'GUEST', value: Gitlab::Access::GUEST - value 'REPORTER', value: Gitlab::Access::REPORTER - value 'DEVELOPER', value: Gitlab::Access::DEVELOPER - value 'MAINTAINER', value: Gitlab::Access::MAINTAINER - value 'OWNER', value: Gitlab::Access::OWNER + value 'NO_ACCESS', value: Gitlab::Access::NO_ACCESS, description: 'No access' + value 'MINIMAL_ACCESS', value: Gitlab::Access::MINIMAL_ACCESS, description: 'Minimal access' + value 'GUEST', value: Gitlab::Access::GUEST, description: 'Guest access' + value 'REPORTER', value: Gitlab::Access::REPORTER, description: 'Reporter access' + value 'DEVELOPER', value: Gitlab::Access::DEVELOPER, description: 'Developer access' + value 'MAINTAINER', value: Gitlab::Access::MAINTAINER, description: 'Maintainer access' + value 'OWNER', value: Gitlab::Access::OWNER, description: 'Owner access' end end diff --git a/app/graphql/types/board_type.rb b/app/graphql/types/board_type.rb index d125ce352c4..f33f3f5e537 100644 --- a/app/graphql/types/board_type.rb +++ b/app/graphql/types/board_type.rb @@ -5,7 +5,7 @@ module Types graphql_name 'Board' description 'Represents a project or group issue board' accepts ::Board - authorize :read_board + authorize :read_issue_board present_using BoardPresenter diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index 494d9875ce4..e538b5e4718 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -23,6 +23,14 @@ module Emails mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason)) end + def change_in_merge_request_draft_status_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil) + setup_merge_request_mail(merge_request_id, recipient_id) + + @updated_by_user = User.find(updated_by_user_id) + + mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason)) + end + # rubocop: disable CodeReuse/ActiveRecord def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_ids, updated_by_user_id, reason = nil) setup_merge_request_mail(merge_request_id, recipient_id) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 36e61b0d9ec..f9abe6a0acb 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -253,9 +253,10 @@ module Ci end def can_pick?(build) - return false if self.ref_protected? && !build.protected? - - assignable_for?(build.project_id) && accepting_tags?(build) + # Run `matches_build?` checks before, since they are cheaper than + # `assignable_for?`. + # + matches_build?(build) && assignable_for?(build.project_id) end def only_for?(project) @@ -266,6 +267,16 @@ module Ci token[0...8] if token end + def tag_list + return super unless Feature.enabled?(:ci_preload_runner_tags, default_enabled: :yaml) + + if tags.loaded? + tags.map(&:name) + else + super + end + end + def has_tags? tag_list.any? end @@ -305,8 +316,10 @@ module Ci end def pick_build!(build) - if can_pick?(build) - tick_runner_queue + if Feature.enabled?(:ci_reduce_queries_when_ticking_runner_queue, self, default_enabled: :yaml) + tick_runner_queue if matches_build?(build) + else + tick_runner_queue if can_pick?(build) end end @@ -370,6 +383,13 @@ module Ci end end + # TODO: choose a better name and consider splitting this method into two + def matches_build?(build) + return false if self.ref_protected? && !build.protected? + + accepting_tags?(build) + end + def accepting_tags?(build) (run_untagged? || build.has_tags?) && (build.tag_list - tag_list).empty? end diff --git a/app/policies/concerns/readonly_abilities.rb b/app/policies/concerns/readonly_abilities.rb index a267e963541..0303d4cff14 100644 --- a/app/policies/concerns/readonly_abilities.rb +++ b/app/policies/concerns/readonly_abilities.rb @@ -17,7 +17,7 @@ module ReadonlyAbilities READONLY_FEATURES = %i[ issue - list + issue_board_list merge_request label milestone diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 7dd88fcc1ff..53286cf1fdf 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -97,9 +97,9 @@ class GroupPolicy < BasePolicy rule { can?(:read_group) }.policy do enable :read_milestone - enable :read_list + enable :read_issue_board_list enable :read_label - enable :read_board + enable :read_issue_board enable :read_group_member enable :read_custom_emoji end @@ -122,9 +122,9 @@ class GroupPolicy < BasePolicy rule { reporter }.policy do enable :reporter_access enable :read_container_image - enable :admin_board + enable :admin_issue_board enable :admin_label - enable :admin_list + enable :admin_issue_board_list enable :admin_issue enable :read_metrics_dashboard_annotation enable :read_prometheus diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index c15c4682654..de80f2f72b8 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -204,8 +204,8 @@ class ProjectPolicy < BasePolicy rule { can?(:guest_access) }.policy do enable :read_project enable :create_merge_request_in - enable :read_board - enable :read_list + enable :read_issue_board + enable :read_issue_board_list enable :read_wiki enable :read_issue enable :read_label @@ -231,7 +231,7 @@ class ProjectPolicy < BasePolicy rule { guest & can?(:read_container_image) }.enable :build_read_container_image rule { can?(:reporter_access) }.policy do - enable :admin_board + enable :admin_issue_board enable :download_code enable :read_statistics enable :download_wiki_code @@ -240,7 +240,7 @@ class ProjectPolicy < BasePolicy enable :reopen_issue enable :admin_issue enable :admin_label - enable :admin_list + enable :admin_issue_board_list enable :admin_issue_link enable :read_commit_status enable :read_build @@ -319,7 +319,7 @@ class ProjectPolicy < BasePolicy rule { can?(:developer_access) }.policy do enable :create_package - enable :admin_board + enable :admin_issue_board enable :admin_merge_request enable :admin_milestone enable :update_merge_request @@ -369,7 +369,7 @@ class ProjectPolicy < BasePolicy rule { can?(:maintainer_access) }.policy do enable :destroy_package - enable :admin_board + enable :admin_issue_board enable :push_to_delete_protected_branch enable :update_snippet enable :admin_snippet @@ -429,8 +429,8 @@ class ProjectPolicy < BasePolicy rule { issues_disabled }.policy do prevent(*create_read_update_admin_destroy(:issue)) - prevent(*create_read_update_admin_destroy(:board)) - prevent(*create_read_update_admin_destroy(:list)) + prevent(*create_read_update_admin_destroy(:issue_board)) + prevent(*create_read_update_admin_destroy(:issue_board_list)) end rule { merge_requests_disabled | repository_disabled }.policy do @@ -507,8 +507,8 @@ class ProjectPolicy < BasePolicy rule { can?(:public_access) }.policy do enable :read_package enable :read_project - enable :read_board - enable :read_list + enable :read_issue_board + enable :read_issue_board_list enable :read_wiki enable :read_label enable :read_milestone diff --git a/app/services/boards/lists/update_service.rb b/app/services/boards/lists/update_service.rb index 4a463372c82..e2d9c371ca2 100644 --- a/app/services/boards/lists/update_service.rb +++ b/app/services/boards/lists/update_service.rb @@ -47,11 +47,11 @@ module Boards end def can_read?(list) - Ability.allowed?(current_user, :read_list, parent) + Ability.allowed?(current_user, :read_issue_board_list, parent) end def can_admin?(list) - Ability.allowed?(current_user, :admin_list, parent) + Ability.allowed?(current_user, :admin_issue_board_list, parent) end end end diff --git a/app/services/ci/update_build_queue_service.rb b/app/services/ci/update_build_queue_service.rb index 6b21a6aaa57..cf629b879b3 100644 --- a/app/services/ci/update_build_queue_service.rb +++ b/app/services/ci/update_build_queue_service.rb @@ -10,6 +10,8 @@ module Ci def tick_for(build, runners, metrics) runners = runners.with_recent_runner_queue + runners = runners.with_tags if Feature.enabled?(:ci_preload_runner_tags, default_enabled: :yaml) + metrics.observe_active_runners(-> { runners.to_a.size }) runners.each do |runner| diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 6745143de22..38df3ee0e43 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -154,11 +154,20 @@ module MergeRequests elsif old_title_wip && !new_title_wip # Unmarked as Draft/WIP # + notify_draft_status_changed(merge_request) + merge_request_activity_counter .track_unmarked_as_draft_action(user: current_user) end end + def notify_draft_status_changed(merge_request) + notification_service.async.change_in_merge_request_draft_status( + merge_request, + current_user + ) + end + def handle_milestone_change(merge_request) return if skip_milestone_email diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index ff652cc2098..1cd4b5407ef 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -189,6 +189,20 @@ class NotificationService end end + def change_in_merge_request_draft_status(merge_request, current_user) + recipients = NotificationRecipients::BuildService.build_recipients(merge_request, current_user, action: "draft_status_change") + + recipients.each do |recipient| + mailer.send( + :change_in_merge_request_draft_status_email, + recipient.user.id, + merge_request.id, + current_user.id, + recipient.reason + ).deliver_later + end + end + # When a merge request is found to be unmergeable, we should send an email to: # # * mr author diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index ababbdc7eb9..8c0893adaaa 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -2,7 +2,7 @@ - group = local_assigns.fetch(:group, false) -# TODO: Move group_id and can_admin_list to the board store See: https://gitlab.com/gitlab-org/gitlab/-/issues/213082 -- can_admin_list = can?(current_user, :admin_list, current_board_parent) == true +- can_admin_list = can?(current_user, :admin_issue_board_list, current_board_parent) == true - @no_breadcrumb_container = true - @no_container = true - @content_class = "issue-boards-content js-focus-mode-board" diff --git a/app/views/shared/boards/_switcher.html.haml b/app/views/shared/boards/_switcher.html.haml index 58e877f20fe..18e0ca20cf7 100644 --- a/app/views/shared/boards/_switcher.html.haml +++ b/app/views/shared/boards/_switcher.html.haml @@ -7,7 +7,7 @@ milestone_path: milestones_filter_path(milestone_filter_opts), board_base_url: board_base_url, has_missing_boards: (!multiple_boards_available? && current_board_parent.boards.size > 1).to_s, - can_admin_board: can?(current_user, :admin_board, parent).to_s, + can_admin_board: can?(current_user, :admin_issue_board, parent).to_s, multiple_issue_boards_available: parent.multiple_issue_boards_available?.to_s, labels_path: labels_filter_path_with_defaults(only_group_labels: true, include_descendant_groups: true), labels_web_url: parent.is_a?(Project) ? project_labels_path(@project) : group_labels_path(@group), diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 0a77f59e1e0..bfa31206a85 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -5,7 +5,7 @@ - placeholder = local_assigns[:placeholder] || _('Search or filter results...') - is_not_boards_modal_or_productivity_analytics = type != :boards_modal && type != :productivity_analytics - block_css_class = is_not_boards_modal_or_productivity_analytics ? 'row-content-block second-block' : '' -- user_can_admin_list = board && can?(current_user, :admin_list, board.resource_parent) +- user_can_admin_list = board && can?(current_user, :admin_issue_board_list, board.resource_parent) .issues-filters{ class: ("w-100" if type == :boards_modal) } .issues-details-filters.filtered-search-block.d-flex.flex-column.flex-lg-row{ class: block_css_class, "v-pre" => type == :boards_modal } @@ -202,7 +202,7 @@ - else = render 'shared/issuable/board_create_list_dropdown', board: board - if @project - #js-add-issues-btn{ data: { can_admin_list: can?(current_user, :admin_list, @project) } } + #js-add-issues-btn{ data: { can_admin_list: can?(current_user, :admin_issue_board_list, @project) } } #js-toggle-focus-btn - elsif is_not_boards_modal_or_productivity_analytics && show_sorting_dropdown = render 'shared/issuable/sort_dropdown' diff --git a/changelogs/unreleased/15332-notify-when-merge-request-draft-status-changes.yml b/changelogs/unreleased/15332-notify-when-merge-request-draft-status-changes.yml new file mode 100644 index 00000000000..eace8a9a2f8 --- /dev/null +++ b/changelogs/unreleased/15332-notify-when-merge-request-draft-status-changes.yml @@ -0,0 +1,5 @@ +--- +title: Send notifications to subscribers when merge request draft status removed +merge_request: 55444 +author: +type: changed diff --git a/changelogs/unreleased/fp-reduce-queries-when-ticking-runner-queue.yml b/changelogs/unreleased/fp-reduce-queries-when-ticking-runner-queue.yml new file mode 100644 index 00000000000..ac43d37ebeb --- /dev/null +++ b/changelogs/unreleased/fp-reduce-queries-when-ticking-runner-queue.yml @@ -0,0 +1,5 @@ +--- +title: Reduce queries when ticking runner queue +merge_request: 55496 +author: +type: performance diff --git a/changelogs/unreleased/preload-runner-tags.yml b/changelogs/unreleased/preload-runner-tags.yml new file mode 100644 index 00000000000..1b733ce4113 --- /dev/null +++ b/changelogs/unreleased/preload-runner-tags.yml @@ -0,0 +1,5 @@ +--- +title: Preload runner tags for `UpdateBuildQueueService` +merge_request: 55543 +author: +type: performance diff --git a/config/feature_flags/development/ci_preload_runner_tags.yml b/config/feature_flags/development/ci_preload_runner_tags.yml new file mode 100644 index 00000000000..d91bd788d41 --- /dev/null +++ b/config/feature_flags/development/ci_preload_runner_tags.yml @@ -0,0 +1,8 @@ +--- +name: ci_preload_runner_tags +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55543 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323243 +milestone: '13.10' +type: development +group: group::memory +default_enabled: false diff --git a/config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml b/config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml new file mode 100644 index 00000000000..906eb83aa01 --- /dev/null +++ b/config/feature_flags/development/ci_reduce_queries_when_ticking_runner_queue.yml @@ -0,0 +1,8 @@ +--- +name: ci_reduce_queries_when_ticking_runner_queue +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55496 +rollout_issue_url: +milestone: '13.10' +type: development +group: group::continuous integration +default_enabled: false diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md index aae31e5cd9d..74937b2c8c6 100644 --- a/doc/administration/geo/replication/datatypes.md +++ b/doc/administration/geo/replication/datatypes.md @@ -118,7 +118,7 @@ We use PostgreSQL's own replication functionality to replicate data from the **p We use Redis both as a cache store and to hold persistent data for our background jobs system. Because both use-cases has data that are exclusive to the same Geo node, we don't replicate it between nodes. -Elasticsearch is an optional database, that can enable advanced searching capabilities, like improved Global Search +Elasticsearch is an optional database, that can enable advanced searching capabilities, like improved Advanced Search in both source-code level and user generated content in Issues / Merge-Requests and discussions. Currently it's not supported in Geo. diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md index 27e8b13812f..206ad7e742b 100644 --- a/doc/administration/instance_limits.md +++ b/doc/administration/instance_limits.md @@ -526,7 +526,7 @@ amount of memory during indexing. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201826) in GitLab 12.8. -You can set a limit on the content of text fields indexed for Global Search. +You can set a limit on the content of text fields indexed for Advanced Search. Setting a maximum helps to reduce the load of the indexing processes. If any text field exceeds this limit then the text will be truncated to this number of characters and the rest will not be indexed and hence will not be searchable. diff --git a/doc/administration/logs.md b/doc/administration/logs.md index 17ecb324417..8b6617210a4 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -87,7 +87,7 @@ In addition, the log contains the originating IP address, (`remote_ip`), the user's ID (`user_id`), and username (`username`). Some endpoints such as `/search` may make requests to Elasticsearch if using -[Advanced Search](../user/search/advanced_global_search.md). These +[Advanced Search](../user/search/advanced_search.md). These additionally log `elasticsearch_calls` and `elasticsearch_call_duration_s`, which correspond to: diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 62472b4590b..d995b57f275 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -4964,12 +4964,13 @@ Access level to a resource. | Value | Description | | ----- | ----------- | -| `DEVELOPER` | | -| `GUEST` | | -| `MAINTAINER` | | -| `NO_ACCESS` | | -| `OWNER` | | -| `REPORTER` | | +| `DEVELOPER` | Developer access | +| `GUEST` | Guest access | +| `MAINTAINER` | Maintainer access | +| `MINIMAL_ACCESS` | Minimal access | +| `NO_ACCESS` | No access | +| `OWNER` | Owner access | +| `REPORTER` | Reporter access | ### AlertManagementAlertSort diff --git a/doc/api/search.md b/doc/api/search.md index 9e1407fdffd..6e9714fba2b 100644 --- a/doc/api/search.md +++ b/doc/api/search.md @@ -12,7 +12,7 @@ type: reference, api Every API call to search must be authenticated. -## Global Search API +## Advanced Search API Search globally across the GitLab instance. diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md index e5d69acc2e5..e076d305251 100644 --- a/doc/development/elasticsearch.md +++ b/doc/development/elasticsearch.md @@ -184,7 +184,7 @@ If the current version is `v12p1`, and we need to create a new version for `v12p 1. Change the namespace for files under `v12p1` folder from `Latest` to `V12p1` 1. Make changes to files under the `latest` folder as needed -## Creating a new Global Search migration +## Creating a new Advanced Search migration > This functionality was introduced by [#234046](https://gitlab.com/gitlab-org/gitlab/-/issues/234046). diff --git a/doc/integration/README.md b/doc/integration/README.md index 9ae75019efb..9c3d39327f8 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -59,8 +59,7 @@ GitLab can be integrated with the following enhancements: or [Kroki](../administration/integration/kroki.md) to use diagrams in AsciiDoc and Markdown documents. - Attach merge requests to [Trello](trello_power_up.md) cards. - Enable integrated code intelligence powered by [Sourcegraph](sourcegraph.md). -- Add [Elasticsearch](elasticsearch.md) for [Advanced Search](../user/search/advanced_global_search.md), - [Advanced System Search](../user/search/advanced_search_syntax.md), and faster searching. +- Add [Elasticsearch](elasticsearch.md) for [Advanced Search](../user/search/advanced_search.md). ## Integrations diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index 6a37514316d..164ec2d32bc 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -11,10 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w This document describes how to enable Advanced Search. After Advanced Search is enabled, you'll have the benefit of fast search response times -and the advantage of the following special searches: - -- [Advanced Search](../user/search/advanced_global_search.md) -- [Advanced Search Syntax](../user/search/advanced_search_syntax.md) +and the advantage of the [special searches](../user/search/advanced_search.md). ## Version requirements diff --git a/doc/subscriptions/bronze_starter.md b/doc/subscriptions/bronze_starter.md index 7c80c7c336b..d66acb4b3f2 100644 --- a/doc/subscriptions/bronze_starter.md +++ b/doc/subscriptions/bronze_starter.md @@ -107,8 +107,7 @@ the tiers are no longer mentioned in GitLab documentation: - Search: - [Filtering merge requests by approvers](../user/search/index.md#filtering-merge-requests-by-approvers) - [Filtering merge requests by "approved by"](../user/search/index.md#filtering-merge-requests-by-approved-by) - - [Advanced Global Search (Elasticsearch)](../user/search/advanced_global_search.md) - - [Advanced Search Syntax](../user/search/advanced_search_syntax.md) + - [Advanced Search (Elasticsearch)](../user/search/advanced_search.md) - [Service Desk](../user/project/service_desk.md) - [Storage usage statistics](../user/usage_quotas.md#storage-usage-statistics) @@ -130,7 +129,7 @@ Bronze-level subscribers: - [Group iterations API](../api/group_iterations.md) - Project milestones API: [Get all burndown chart events for a single milestone](../api/milestones.md#get-all-burndown-chart-events-for-a-single-milestone) - [Project iterations API](../api/iterations.md) - - Fields in the [Search API](../api/search.md) available only to [Advanced Global Search (Elasticsearch)](../integration/elasticsearch.md) users + - Fields in the [Search API](../api/search.md) available only to [Advanced Search (Elasticsearch)](../integration/elasticsearch.md) users - Fields in the [Merge requests API](../api/merge_requests.md) for [merge request approvals](../user/project/merge_requests/merge_request_approvals.md) - Fields in the [Protected branches API](../api/protected_branches.md) that specify users or groups allowed to merge - [Merge request approvals API](../api/merge_request_approvals.md) diff --git a/doc/topics/git/cherry_picking.md b/doc/topics/git/cherry_picking.md new file mode 100644 index 00000000000..5a0867371bb --- /dev/null +++ b/doc/topics/git/cherry_picking.md @@ -0,0 +1,53 @@ +--- +stage: Create +group: Source Code +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +comments: false +--- + +# Cherry Pick + +Given an existing commit on one branch, apply the change to another branch. + +This can be useful for backporting bug fixes to previous release branches. Make +the commit on the default branch, and then cherry pick it into the release branch. + +## Sample workflow + +1. Check out a new `stable` branch from the default branch: + + ```shell + git checkout master + git checkout -b stable + ``` + +1. Change back to the default branch: + + ```shell + git checkout master + ``` + +1. Make any required changes, then commit the changes: + + ```shell + git add changed_file.rb + git commit -m 'Fix bugs in changed_file.rb' + ``` + +1. Review the commit log and copy the SHA of the latest commit: + + ```shell + git log + ``` + +1. Check out the `stable` branch: + + ```shell + git checkout stable + ``` + +1. Cherry pick the commit by using the SHA copied previously: + + ```shell + git cherry-pick + ``` diff --git a/doc/university/training/topics/cherry_picking.md b/doc/university/training/topics/cherry_picking.md index 2f806ad2590..f0f815baa94 100644 --- a/doc/university/training/topics/cherry_picking.md +++ b/doc/university/training/topics/cherry_picking.md @@ -1,36 +1,8 @@ --- -stage: none -group: unassigned -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments -comments: false +redirect_to: '../../../topics/git/cherry_picking.md' --- -# Cherry Pick +This document was moved to [another location](../../../topics/git/cherry_picking.md). -- Given an existing commit on one branch, apply the change to another branch -- Useful for backporting bug fixes to previous release branches -- Make the commit on the master branch and pick in to stable - -## Cherry Pick sample workflow - -1. Check out a new 'stable' branch from 'master' -1. Change back to 'master' -1. Edit '`cherry_pick.rb`' and commit the changes. -1. Check commit log to get the commit SHA -1. Check out the 'stable' branch -1. Cherry pick the commit using the SHA obtained earlier - -```shell -git checkout master -git checkout -b stable -git checkout master - -# Edit `cherry_pick.rb` -git add cherry_pick.rb -git commit -m 'Fix bugs in cherry_pick.rb' -git log -# Copy commit SHA -git checkout stable - -git cherry-pick -``` + + diff --git a/doc/university/training/topics/tags.md b/doc/university/training/topics/tags.md index bb2e3e9b208..28fd8400d99 100644 --- a/doc/university/training/topics/tags.md +++ b/doc/university/training/topics/tags.md @@ -4,5 +4,5 @@ redirect_to: '../../../topics/git/tags.md' This document was moved to [another location](../../../topics/git/tags.md). - + diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 1d72426a1d4..ed75f318448 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -1469,7 +1469,7 @@ Kubernetes API, giving you access to more advanced querying capabilities. Log data is deleted after 30 days, using [Curator](https://www.elastic.co/guide/en/elasticsearch/client/curator/5.5/about.html). The Elastic Stack cluster application is intended as a log aggregation solution -and is not related to our [Advanced Search](../search/advanced_global_search.md) +and is not related to our [Advanced Search](../search/advanced_search.md) functionality, which uses a separate Elasticsearch cluster. To enable log shipping: diff --git a/doc/user/index.md b/doc/user/index.md index 47a9699543a..dfbba7e9c19 100644 --- a/doc/user/index.md +++ b/doc/user/index.md @@ -61,7 +61,7 @@ With GitLab Enterprise Edition, you can also: - [Multiple Issue Boards](project/issue_board.md#multiple-issue-boards). - Create formal relationships between issues with [Related Issues](project/issues/related_issues.md). - Use [Burndown Charts](project/milestones/burndown_and_burnup_charts.md) to track progress during a sprint or while working on a new version of their software. -- Leverage [Elasticsearch](../integration/elasticsearch.md) with [Advanced Search](search/advanced_global_search.md) and [Advanced Search Syntax](search/advanced_search_syntax.md) for faster, more advanced code search across your entire GitLab instance. +- Leverage [Elasticsearch](../integration/elasticsearch.md) with [Advanced Search](search/advanced_search.md) for faster, more advanced code search across your entire GitLab instance. - [Authenticate users with Kerberos](../integration/kerberos.md). - [Mirror a repository](project/repository/repository_mirroring.md) from elsewhere on your local server. - View your entire CI/CD pipeline involving more than one project with [Multiple-Project Pipelines](../ci/multi_project_pipelines.md). diff --git a/doc/user/project/merge_requests/merge_request_approvals.md b/doc/user/project/merge_requests/merge_request_approvals.md index a72ad8784e7..eab84aed077 100644 --- a/doc/user/project/merge_requests/merge_request_approvals.md +++ b/doc/user/project/merge_requests/merge_request_approvals.md @@ -79,7 +79,8 @@ An individual user can be added as an approver for a project if they are a membe - The project's immediate parent group. - A group that has access to the project via a [share](../members/share_project_with_groups.md). -A group of users can also be added as approvers. In the future, group approvers may be +A group of users can also be added as approvers, though they will only count as approvers if +they have direct membership to the group. In the future, group approvers may be [restricted to only groups with share access to the project](https://gitlab.com/gitlab-org/gitlab/-/issues/2048). If a user is added as an individual approver and is also part of a group approver, diff --git a/doc/user/search/advanced_global_search.md b/doc/user/search/advanced_global_search.md index 2d1a05cd966..44af6f90cd9 100644 --- a/doc/user/search/advanced_global_search.md +++ b/doc/user/search/advanced_global_search.md @@ -1,70 +1,8 @@ --- -stage: Enablement -group: Global Search -info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments" -type: reference +redirect_to: advanced_search.md --- -# Advanced Search **(PREMIUM)** +This document was moved to [another location](advanced_search.md). -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/109) in GitLab 8.4. -> - [Moved](../../subscriptions/bronze_starter.md) to GitLab Premium in 13.9. - -NOTE: -Advanced Search (powered by Elasticsearch) is enabled for Bronze and above on GitLab.com since 2020-07-10. - -Leverage Elasticsearch for faster, more advanced code search across your entire -GitLab instance. - -This is the user documentation. To install and configure Elasticsearch, -visit the [administrator documentation](../../integration/elasticsearch.md). - -## Overview - -The Advanced Search in GitLab is a powerful search service that saves -you time. Instead of creating duplicate code and wasting time, you can -now search for code within other projects that can help your own project. - -GitLab leverages the search capabilities of [Elasticsearch](https://www.elastic.co/elasticsearch/) and enables it when -searching in: - -- Projects -- Issues -- Merge requests -- Milestones -- Comments -- Code -- Commits -- Wiki -- Users - -## Use cases - -The Advanced Search can be useful in various scenarios. - -### Faster searches - -Advanced Search is based on Elasticsearch, which is a purpose built full text search engine that can be horizontally scaled so that it can provide search results in 1-2 seconds in most cases. - -### Promote innersourcing - -Your company may consist of many different developer teams each of which has -their own group where the various projects are hosted. Some of your applications -may be connected to each other, so your developers need to instantly search -throughout the GitLab instance and find the code they search for. - -## Searching globally - -Just use the search as before and GitLab will show you matching code from each -project you have access to. - -![Advanced Search](img/advanced_global_search.png) - -You can also use the [Advanced Search Syntax](advanced_search_syntax.md) which -provides some useful queries. - -NOTE: -Elasticsearch has only data for the default branch. That means that if you go -to the repository tree and switch the branch from the default to something else, -then the "Code" tab in the search result page will be served by the basic -search even if Elasticsearch is enabled. + + diff --git a/doc/user/search/advanced_search.md b/doc/user/search/advanced_search.md new file mode 100644 index 00000000000..d11f02addea --- /dev/null +++ b/doc/user/search/advanced_search.md @@ -0,0 +1,115 @@ +--- +stage: Enablement +group: Global Search +info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments" +type: reference +--- + +# GitLab Advanced Search **(PREMIUM)** + +> - Moved to GitLab Premium in 13.9. + +NOTE: +This is the user documentation. To configure the Advanced Search, +visit the [administrator documentation](../../integration/elasticsearch.md). + +GitLab Advanced Search expands on the Basic Search with an additional set of +features for faster, more advanced searches across the entire GitLab instance +when searching in: + +- Projects +- Issues +- Merge requests +- Milestones +- Epics +- Comments +- Code +- Commits +- Wiki +- Users + +The Advanced Search can be useful in various scenarios: + +- **Faster searches:** + Advanced Search is based on Elasticsearch, which is a purpose-built full + text search engine that can be horizontally scaled so that it can provide + search results in 1-2 seconds in most cases. +- **Promote innersourcing:** + Your company may consist of many different developer teams each of which has + their own group where the various projects are hosted. Some of your applications + may be connected to each other, so your developers need to instantly search + throughout the GitLab instance and find the code they search for. + +## Use the Advanced Search syntax + +Elasticsearch has only data for the default branch. That means that if you go +to the repository tree and switch the branch from the default to something else, +then the "Code" tab in the search result page will be served by the basic +search even if Elasticsearch is enabled. + +The Advanced Search syntax supports fuzzy or exact search queries with prefixes, +boolean operators, and much more. Use the search as before and GitLab will show +you matching code from each project you have access to. + +![Advanced Search](img/advanced_search_v13.10.png) + +Full details can be found in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/5.3/query-dsl-simple-query-string-query.html#_simple_query_string_syntax), but +here's a quick guide: + +- Searches look for all the words in a query, in any order - e.g.: searching + issues for [`display bug`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=display+bug&group_id=9970&project_id=278964) and [`bug display`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+Display&group_id=9970&project_id=278964) will return the same results. +- To find the exact phrase (stemming still applies), use double quotes: [`"display bug"`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=%22display+bug%22&group_id=9970&project_id=278964) +- To find bugs not mentioning display, use `-`: [`bug -display`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+-display&group_id=9970&project_id=278964) +- To find a bug in display or banner, use `|`: [`bug display | banner`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+display+%7C+banner&group_id=9970&project_id=278964) +- To group terms together, use parentheses: [`bug | (display +banner)`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+%7C+%28display+%2Bbanner%29&group_id=9970&project_id=278964) +- To match a partial word, use `*`. In this example, I want to find bugs with any 500 errors. : [`bug error 50*`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+error+50*&group_id=9970&project_id=278964) +- To use one of symbols above literally, escape the symbol with a preceding `\`: [`argument \-last`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=argument+%5C-last&group_id=9970&project_id=278964) + +## Syntax search filters + +Advanced Search also supports the use of filters. The available filters are: + +- `filename`: Filters by filename. You can use the glob (`*`) operator for fuzzy matching. +- `path`: Filters by path. You can use the glob (`*`) operator for fuzzy matching. +- `extension`: Filters by extension in the filename. Please write the extension without a leading dot. Exact match only. +- `blob`: Filters by Git `object ID`. Exact match only. + +To use them, add them to your keyword in the format `:` without +any spaces between the colon (`:`) and the value. When no keyword is provided, an asterisk (`*`) will be used as the keyword. + +Examples: + +- Finding a file with any content named `search_results.rb`: [`* filename:search_results.rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=*+filename%3Asearch_results.rb&group_id=9970&project_id=278964) +- The leading asterisk (`*`) can be ignored in the case above: [`filename:search_results.rb`](https://gitlab.com/search?group_id=9970&project_id=278964&scope=blobs&search=filename%3Asearch_results.rb) +- Finding a file named `found_blob_spec.rb` with the text `CHANGELOG` inside of it: [`CHANGELOG filename:found_blob_spec.rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=CHANGELOG+filename%3Afound_blob_spec.rb&group_id=9970&project_id=278964) +- Finding the text `EpicLinks` inside files with the `.rb` extension: [`EpicLinks extension:rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=EpicLinks+extension%3Arb&group_id=9970&project_id=278964) +- Finding any file with the `.yaml` extension: [`extension:yaml`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=extension%3Ayaml&group_id=9970&project_id=278964) +- Finding the text `Sidekiq` in a file, when that file is in a path that includes `elastic`: [`Sidekiq path:elastic`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=Sidekiq+path%3Aelastic&group_id=9970&project_id=278964) +- Finding any file in a path that includes `elasticsearch`: [`path:elasticsearch`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=path%3Aelasticsearch&group_id=9970&project_id=278964) +- Finding the files represented by the Git object ID `998707b421c89bd9a3063333f9f728ef3e43d101`: [`* blob:998707b421c89bd9a3063333f9f728ef3e43d101`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=false&scope=blobs&repository_ref=&search=*+blob%3A998707b421c89bd9a3063333f9f728ef3e43d101&group_id=9970) +- Syntax filters can be combined for complex filtering. Finding any file starting with `search` containing `eventHub` and with the `.js` extension: [`eventHub filename:search* extension:js`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=eventHub+filename%3Asearch*+extension%3Ajs&group_id=9970&project_id=278964) + +### Excluding filters + +[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31684) in GitLab Starter 13.3. + +Filters can be inverted to **filter out** results from the result set, by prefixing the filter name with a `-` (hyphen) character, such as: + +- `-filename` +- `-path` +- `-extension` +- `-blob` + +Examples: + +- Finding `rails` in all files but `Gemfile.lock`: [`rails -filename:Gemfile.lock`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=rails+-filename%3AGemfile.lock&group_id=9970&project_id=278964) +- Finding `success` in all files excluding `.po|pot` files: [`success -filename:*.po*`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=success+-filename%3A*.po*&group_id=9970&project_id=278964) +- Finding `import` excluding minified JavaScript (`.min.js`) files: [`import -extension:min.js`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=import+-extension%3Amin.js&group_id=9970&project_id=278964) +- Finding `docs` for all files outside the `docs/` folder: [`docs -path:docs/`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=docs+-path%3Adocs%2F&group_id=9970&project_id=278964) + +## Search by issue or merge request ID + +You can search a specific issue or merge request by its ID with a special prefix. + +- To search by issue ID, use prefix `#` followed by issue ID. For example, [#23456](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=%2323456&group_id=9970&project_id=278964) +- To search by merge request ID, use prefix `!` followed by merge request ID. For example [!23456](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=merge_requests&repository_ref=&search=%2123456&group_id=9970&project_id=278964) diff --git a/doc/user/search/advanced_search_syntax.md b/doc/user/search/advanced_search_syntax.md index 19f7305124e..44af6f90cd9 100644 --- a/doc/user/search/advanced_search_syntax.md +++ b/doc/user/search/advanced_search_syntax.md @@ -1,90 +1,8 @@ --- -stage: Enablement -group: Global Search -info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments" -type: reference +redirect_to: advanced_search.md --- -# Advanced Search Syntax **(PREMIUM)** +This document was moved to [another location](advanced_search.md). -> - Introduced in [GitLab](https://about.gitlab.com/pricing/) 9.2. -> - [Moved](../../subscriptions/bronze_starter.md) to GitLab Premium in 13.9. - -Use advanced queries for more targeted search results. - -This is the user documentation. To install and configure Elasticsearch, -visit the [administrator documentation](../../integration/elasticsearch.md). - -## Overview - -The Advanced Search Syntax is a subset of the -[Advanced Search](advanced_global_search.md), which you can use if you -want to have more specific search results. - -Advanced Search only supports searching the [default branch](../project/repository/branches/index.md#default-branch). - -## Using the Advanced Search Syntax - -The Advanced Search Syntax supports fuzzy or exact search queries with prefixes, -boolean operators, and much more. - -Full details can be found in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/5.3/query-dsl-simple-query-string-query.html#_simple_query_string_syntax), but -here's a quick guide: - -- Searches look for all the words in a query, in any order - e.g.: searching - issues for [`display bug`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=display+bug&group_id=9970&project_id=278964) and [`bug display`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+Display&group_id=9970&project_id=278964) will return the same results. -- To find the exact phrase (stemming still applies), use double quotes: [`"display bug"`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=%22display+bug%22&group_id=9970&project_id=278964) -- To find bugs not mentioning display, use `-`: [`bug -display`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+-display&group_id=9970&project_id=278964) -- To find a bug in display or banner, use `|`: [`bug display | banner`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+display+%7C+banner&group_id=9970&project_id=278964) -- To group terms together, use parentheses: [`bug | (display +banner)`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+%7C+%28display+%2Bbanner%29&group_id=9970&project_id=278964) -- To match a partial word, use `*`. In this example, I want to find bugs with any 500 errors. : [`bug error 50*`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=bug+error+50*&group_id=9970&project_id=278964) -- To use one of symbols above literally, escape the symbol with a preceding `\`: [`argument \-last`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=argument+%5C-last&group_id=9970&project_id=278964) - -### Syntax search filters - -The Advanced Search Syntax also supports the use of filters. The available filters are: - -- filename: Filters by filename. You can use the glob (`*`) operator for fuzzy matching. -- path: Filters by path. You can use the glob (`*`) operator for fuzzy matching. -- extension: Filters by extension in the filename. Please write the extension without a leading dot. Exact match only. -- blob: Filters by Git `object ID`. Exact match only. - -To use them, add them to your keyword in the format `:` without -any spaces between the colon (`:`) and the value. When no keyword is provided, an asterisk (`*`) will be used as the keyword. - -Examples: - -- Finding a file with any content named `search_results.rb`: [`* filename:search_results.rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=*+filename%3Asearch_results.rb&group_id=9970&project_id=278964) -- The leading asterisk (`*`) can be ignored in the case above: [`filename:search_results.rb`](https://gitlab.com/search?group_id=9970&project_id=278964&scope=blobs&search=filename%3Asearch_results.rb) -- Finding a file named `found_blob_spec.rb` with the text `CHANGELOG` inside of it: [`CHANGELOG filename:found_blob_spec.rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=CHANGELOG+filename%3Afound_blob_spec.rb&group_id=9970&project_id=278964) -- Finding the text `EpicLinks` inside files with the `.rb` extension: [`EpicLinks extension:rb`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=EpicLinks+extension%3Arb&group_id=9970&project_id=278964) -- Finding any file with the `.yaml` extension: [`extension:yaml`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=extension%3Ayaml&group_id=9970&project_id=278964) -- Finding the text `Sidekiq` in a file, when that file is in a path that includes `elastic`: [`Sidekiq path:elastic`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=Sidekiq+path%3Aelastic&group_id=9970&project_id=278964) -- Finding any file in a path that includes `elasticsearch`: [`path:elasticsearch`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=path%3Aelasticsearch&group_id=9970&project_id=278964) -- Finding the files represented by the Git object ID `998707b421c89bd9a3063333f9f728ef3e43d101`: [`* blob:998707b421c89bd9a3063333f9f728ef3e43d101`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=false&scope=blobs&repository_ref=&search=*+blob%3A998707b421c89bd9a3063333f9f728ef3e43d101&group_id=9970) -- Syntax filters can be combined for complex filtering. Finding any file starting with `search` containing `eventHub` and with the `.js` extension: [`eventHub filename:search* extension:js`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=eventHub+filename%3Asearch*+extension%3Ajs&group_id=9970&project_id=278964) - -#### Excluding filters - -[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31684) in GitLab Starter 13.3. - -Filters can be inverted to **filter out** results from the result set, by prefixing the filter name with a `-` (hyphen) character, such as: - -- `-filename` -- `-path` -- `-extension` -- `-blob` - -Examples: - -- Finding `rails` in all files but `Gemfile.lock`: [`rails -filename:Gemfile.lock`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=rails+-filename%3AGemfile.lock&group_id=9970&project_id=278964) -- Finding `success` in all files excluding `.po|pot` files: [`success -filename:*.po*`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=success+-filename%3A*.po*&group_id=9970&project_id=278964) -- Finding `import` excluding minified JavaScript (`.min.js`) files: [`import -extension:min.js`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=import+-extension%3Amin.js&group_id=9970&project_id=278964) -- Finding `docs` for all files outside the `docs/` folder: [`docs -path:docs/`](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=blobs&repository_ref=&search=docs+-path%3Adocs%2F&group_id=9970&project_id=278964) - -### Search by issue or merge request ID - -You can search a specific issue or merge request by its ID with a special prefix. - -- To search by issue ID, use prefix `#` followed by issue ID. For example, [#23456](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=issues&repository_ref=&search=%2323456&group_id=9970&project_id=278964) -- To search by merge request ID, use prefix `!` followed by merge request ID. For example [!23456](https://gitlab.com/search?utf8=%E2%9C%93&snippets=&scope=merge_requests&repository_ref=&search=%2123456&group_id=9970&project_id=278964) + + diff --git a/doc/user/search/img/advanced_global_search.png b/doc/user/search/img/advanced_global_search.png deleted file mode 100644 index 4903bbb07e13f9730b2a6b4aec66e0d3f6902e3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15017 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

K@yoH}a1Rr8%ygci~}GUOmzJ_ytMNDTFFD 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 literal 0 HcmV?d00001 diff --git a/doc/user/search/index.md b/doc/user/search/index.md index 79a7827c03b..f327288ea0a 100644 --- a/doc/user/search/index.md +++ b/doc/user/search/index.md @@ -298,13 +298,7 @@ redirected to the commit result and given the option to return to the search res Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance. -[Learn how to use the Advanced Search.](advanced_global_search.md) - -## Advanced Search Syntax **(PREMIUM)** - -Use advanced queries for more targeted search results. - -[Learn how to use the Advanced Search Syntax.](advanced_search_syntax.md) +[Learn how to use the Advanced Search.](advanced_search.md) ## Search settings diff --git a/lib/api/boards.rb b/lib/api/boards.rb index 5fd4ca3546c..79f4b02f26a 100644 --- a/lib/api/boards.rb +++ b/lib/api/boards.rb @@ -30,7 +30,7 @@ module API use :pagination end get '/' do - authorize!(:read_board, user_project) + authorize!(:read_issue_board, user_project) present paginate(board_parent.boards.with_associations), with: Entities::Board end @@ -39,7 +39,7 @@ module API success Entities::Board end get '/:board_id' do - authorize!(:read_board, user_project) + authorize!(:read_issue_board, user_project) present board, with: Entities::Board end @@ -51,7 +51,7 @@ module API requires :name, type: String, desc: 'The board name' end post '/' do - authorize!(:admin_board, board_parent) + authorize!(:admin_issue_board, board_parent) create_board end @@ -64,7 +64,7 @@ module API use :update_params end put '/:board_id' do - authorize!(:admin_board, board_parent) + authorize!(:admin_issue_board, board_parent) update_board end @@ -75,7 +75,7 @@ module API end delete '/:board_id' do - authorize!(:admin_board, board_parent) + authorize!(:admin_issue_board, board_parent) delete_board end @@ -93,7 +93,7 @@ module API use :pagination end get '/lists' do - authorize!(:read_board, user_project) + authorize!(:read_issue_board, user_project) present paginate(board_lists), with: Entities::List end @@ -105,7 +105,7 @@ module API requires :list_id, type: Integer, desc: 'The ID of a list' end get '/lists/:list_id' do - authorize!(:read_board, user_project) + authorize!(:read_issue_board, user_project) present board_lists.find(params[:list_id]), with: Entities::List end @@ -117,7 +117,7 @@ module API use :list_creation_params end post '/lists' do - authorize!(:admin_list, user_project) + authorize!(:admin_issue_board_list, user_project) create_list end @@ -133,7 +133,7 @@ module API put '/lists/:list_id' do list = board_lists.find(params[:list_id]) - authorize!(:admin_list, user_project) + authorize!(:admin_issue_board_list, user_project) move_list(list) end @@ -146,7 +146,7 @@ module API requires :list_id, type: Integer, desc: 'The ID of a board list' end delete "/lists/:list_id" do - authorize!(:admin_list, user_project) + authorize!(:admin_issue_board_list, user_project) list = board_lists.find(params[:list_id]) destroy_list(list) diff --git a/lib/api/group_boards.rb b/lib/api/group_boards.rb index 7425e1bd145..90632048354 100644 --- a/lib/api/group_boards.rb +++ b/lib/api/group_boards.rb @@ -30,7 +30,7 @@ module API use :pagination end get '/' do - authorize!(:read_board, user_group) + authorize!(:read_issue_board, user_group) present paginate(board_parent.boards.with_associations), with: Entities::Board end @@ -39,7 +39,7 @@ module API success Entities::Board end get '/:board_id' do - authorize!(:read_board, user_group) + authorize!(:read_issue_board, user_group) present board, with: Entities::Board end @@ -51,7 +51,7 @@ module API use :update_params end put '/:board_id' do - authorize!(:admin_board, board_parent) + authorize!(:admin_issue_board, board_parent) update_board end @@ -69,7 +69,7 @@ module API use :pagination end get '/lists' do - authorize!(:read_board, user_group) + authorize!(:read_issue_board, user_group) present paginate(board_lists), with: Entities::List end @@ -81,7 +81,7 @@ module API requires :list_id, type: Integer, desc: 'The ID of a list' end get '/lists/:list_id' do - authorize!(:read_board, user_group) + authorize!(:read_issue_board, user_group) present board_lists.find(params[:list_id]), with: Entities::List end @@ -93,7 +93,7 @@ module API use :list_creation_params end post '/lists' do - authorize!(:admin_list, user_group) + authorize!(:admin_issue_board_list, user_group) create_list end @@ -109,7 +109,7 @@ module API put '/lists/:list_id' do list = board_lists.find(params[:list_id]) - authorize!(:admin_list, user_group) + authorize!(:admin_issue_board_list, user_group) move_list(list) end @@ -122,7 +122,7 @@ module API requires :list_id, type: Integer, desc: 'The ID of a board list' end delete "/lists/:list_id" do - authorize!(:admin_list, user_group) + authorize!(:admin_issue_board_list, user_group) list = board_lists.find(params[:list_id]) destroy_list(list) diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb index a7480130e0a..6201cddecb0 100644 --- a/spec/controllers/groups/boards_controller_spec.rb +++ b/spec/controllers/groups/boards_controller_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Groups::BoardsController do expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, group).and_return(false) end it 'returns a not found 404 response' do @@ -74,7 +74,7 @@ RSpec.describe Groups::BoardsController do expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, group).and_return(false) end it 'returns a not found 404 response' do @@ -111,7 +111,7 @@ RSpec.describe Groups::BoardsController do expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_board, group).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, group).and_return(false) end it 'returns a not found 404 response' do diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb index 1ed61e0990f..cde3a8d4761 100644 --- a/spec/controllers/projects/boards_controller_spec.rb +++ b/spec/controllers/projects/boards_controller_spec.rb @@ -34,7 +34,7 @@ RSpec.describe Projects::BoardsController do before do expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) end it 'returns a not found 404 response' do @@ -78,7 +78,7 @@ RSpec.describe Projects::BoardsController do before do expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) end it 'returns a not found 404 response' do @@ -134,7 +134,7 @@ RSpec.describe Projects::BoardsController do before do expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) end it 'returns a not found 404 response' do @@ -172,7 +172,7 @@ RSpec.describe Projects::BoardsController do before do expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) end it 'returns a not found 404 response' do diff --git a/spec/graphql/mutations/boards/update_spec.rb b/spec/graphql/mutations/boards/update_spec.rb index 6f05b21b0b4..da3dfeecd4d 100644 --- a/spec/graphql/mutations/boards/update_spec.rb +++ b/spec/graphql/mutations/boards/update_spec.rb @@ -20,7 +20,7 @@ RSpec.describe Mutations::Boards::Update do subject { mutation.resolve(**mutation_params) } - specify { expect(described_class).to require_graphql_authorizations(:admin_board) } + specify { expect(described_class).to require_graphql_authorizations(:admin_issue_board) } describe '#resolve' do context 'when the user cannot admin the board' do diff --git a/spec/graphql/types/access_level_enum_spec.rb b/spec/graphql/types/access_level_enum_spec.rb index eeb10a50b7e..1b379c56ff9 100644 --- a/spec/graphql/types/access_level_enum_spec.rb +++ b/spec/graphql/types/access_level_enum_spec.rb @@ -6,6 +6,6 @@ RSpec.describe GitlabSchema.types['AccessLevelEnum'] do specify { expect(described_class.graphql_name).to eq('AccessLevelEnum') } it 'exposes all the existing access levels' do - expect(described_class.values.keys).to match_array(%w[NO_ACCESS GUEST REPORTER DEVELOPER MAINTAINER OWNER]) + expect(described_class.values.keys).to match_array(%w[NO_ACCESS MINIMAL_ACCESS GUEST REPORTER DEVELOPER MAINTAINER OWNER]) end end diff --git a/spec/graphql/types/board_type_spec.rb b/spec/graphql/types/board_type_spec.rb index 5ea87d5f473..dca3cfd8aaf 100644 --- a/spec/graphql/types/board_type_spec.rb +++ b/spec/graphql/types/board_type_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe GitlabSchema.types['Board'] do specify { expect(described_class.graphql_name).to eq('Board') } - specify { expect(described_class).to require_graphql_authorizations(:read_board) } + specify { expect(described_class).to require_graphql_authorizations(:read_issue_board) } it 'has specific fields' do expected_fields = %w[id name web_url web_path] diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index f9bf6d2967a..4c8212bf774 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -842,27 +842,50 @@ RSpec.describe Ci::Runner do end describe '#pick_build!' do + let(:build) { create(:ci_build) } + let(:runner) { create(:ci_runner) } + context 'runner can pick the build' do it 'calls #tick_runner_queue' do - ci_build = build(:ci_build) - runner = build(:ci_runner) - allow(runner).to receive(:can_pick?).with(ci_build).and_return(true) - expect(runner).to receive(:tick_runner_queue) - runner.pick_build!(ci_build) + runner.pick_build!(build) end end context 'runner cannot pick the build' do - it 'does not call #tick_runner_queue' do - ci_build = build(:ci_build) - runner = build(:ci_runner) - allow(runner).to receive(:can_pick?).with(ci_build).and_return(false) + before do + build.tag_list = [:docker] + end + it 'does not call #tick_runner_queue' do expect(runner).not_to receive(:tick_runner_queue) - runner.pick_build!(ci_build) + runner.pick_build!(build) + end + end + + context 'build picking improvement enabled' do + before do + stub_feature_flags(ci_reduce_queries_when_ticking_runner_queue: true) + end + + it 'does not check if the build is assignable to a runner' do + expect(runner).not_to receive(:can_pick?) + + runner.pick_build!(build) + end + end + + context 'build picking improvement disabled' do + before do + stub_feature_flags(ci_reduce_queries_when_ticking_runner_queue: false) + end + + it 'checks if the build is assignable to a runner' do + expect(runner).to receive(:can_pick?).and_call_original + + runner.pick_build!(build) end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index f0981469123..d2693a32bdf 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1834,7 +1834,7 @@ RSpec.describe User do end describe '.instance_access_request_approvers_to_be_notified' do - let_it_be(:admin_list) { create_list(:user, 12, :admin, :with_sign_ins) } + let_it_be(:admin_issue_board_list) { create_list(:user, 12, :admin, :with_sign_ins) } it 'returns up to the ten most recently active instance admins' do active_admins_in_recent_sign_in_desc_order = User.admins.active.order_recent_sign_in.limit(10) diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index b07940760dd..60c54f97312 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -64,8 +64,8 @@ RSpec.describe ProjectPolicy do end it 'disables boards and lists permissions' do - expect_disallowed :read_board, :create_board, :update_board - expect_disallowed :read_list, :create_list, :update_list, :admin_list + expect_disallowed :read_issue_board, :create_board, :update_board + expect_disallowed :read_issue_board_list, :create_list, :update_list, :admin_issue_board_list end context 'when external tracker configured' do diff --git a/spec/services/ci/update_build_queue_service_spec.rb b/spec/services/ci/update_build_queue_service_spec.rb index ebccfdc5140..6cac49febda 100644 --- a/spec/services/ci/update_build_queue_service_spec.rb +++ b/spec/services/ci/update_build_queue_service_spec.rb @@ -26,6 +26,24 @@ RSpec.describe Ci::UpdateBuildQueueService do end it_behaves_like 'refreshes runner' + + it 'avoids running redundant queries' do + expect(Ci::Runner).not_to receive(:owned_or_instance_wide) + + subject.execute(build) + end + + context 'when feature flag ci_reduce_queries_when_ticking_runner_queue is disabled' do + before do + stub_feature_flags(ci_reduce_queries_when_ticking_runner_queue: false) + end + + it 'runs redundant queries using `owned_or_instance_wide` scope' do + expect(Ci::Runner).to receive(:owned_or_instance_wide).and_call_original + + subject.execute(build) + end + end end end @@ -97,4 +115,43 @@ RSpec.describe Ci::UpdateBuildQueueService do it_behaves_like 'does not refresh runner' end end + + context 'avoids N+1 queries', :request_store do + let!(:build) { create(:ci_build, pipeline: pipeline, tag_list: %w[a b]) } + let!(:project_runner) { create(:ci_runner, :project, :online, projects: [project], tag_list: %w[a b c]) } + + context 'when ci_preload_runner_tags and ci_reduce_queries_when_ticking_runner_queue are enabled' do + before do + stub_feature_flags( + ci_reduce_queries_when_ticking_runner_queue: true, + ci_preload_runner_tags: true + ) + end + + it 'does execute the same amount of queries regardless of number of runners' do + control_count = ActiveRecord::QueryRecorder.new { subject.execute(build) }.count + + create_list(:ci_runner, 10, :project, :online, projects: [project], tag_list: %w[b c d]) + + expect { subject.execute(build) }.not_to exceed_all_query_limit(control_count) + end + end + + context 'when ci_preload_runner_tags and ci_reduce_queries_when_ticking_runner_queue are disabled' do + before do + stub_feature_flags( + ci_reduce_queries_when_ticking_runner_queue: false, + ci_preload_runner_tags: false + ) + end + + it 'does execute more queries for more runners' do + control_count = ActiveRecord::QueryRecorder.new { subject.execute(build) }.count + + create_list(:ci_runner, 10, :project, :online, projects: [project], tag_list: %w[b c d]) + + expect { subject.execute(build) }.to exceed_all_query_limit(control_count) + end + end + end end diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index edb95840604..a6bd8416e58 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -656,6 +656,48 @@ RSpec.describe MergeRequests::UpdateService, :mailer do end end + context 'when the draft status is changed' do + let!(:non_subscriber) { create(:user) } + let!(:subscriber) do + create(:user) { |u| merge_request.toggle_subscription(u, project) } + end + + before do + project.add_developer(non_subscriber) + project.add_developer(subscriber) + end + + context 'removing draft status' do + before do + merge_request.update_attribute(:title, 'Draft: New Title') + end + + it 'sends notifications for subscribers', :sidekiq_might_not_need_inline do + opts = { title: 'New title' } + + perform_enqueued_jobs do + @merge_request = described_class.new(project, user, opts).execute(merge_request) + end + + should_email(subscriber) + should_not_email(non_subscriber) + end + end + + context 'adding draft status' do + it 'does not send notifications', :sidekiq_might_not_need_inline do + opts = { title: 'Draft: New title' } + + perform_enqueued_jobs do + @merge_request = described_class.new(project, user, opts).execute(merge_request) + end + + should_not_email(subscriber) + should_not_email(non_subscriber) + end + end + end + context 'when the merge request is relabeled' do let!(:non_subscriber) { create(:user) } let!(:subscriber) { create(:user) { |u| label.toggle_subscription(u, project) } } diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 76b06581ff7..36946b59eae 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1868,6 +1868,42 @@ RSpec.describe NotificationService, :mailer do end end + describe '#change_in_merge_request_draft_status' do + let(:merge_request) { create(:merge_request, author: author, source_project: project) } + + let_it_be(:current_user) { create(:user) } + + it 'sends emails to relevant users only', :aggregate_failures do + notification.change_in_merge_request_draft_status(merge_request, current_user) + + merge_request.reviewers.each { |reviewer| should_email(reviewer) } + merge_request.assignees.each { |assignee| should_email(assignee) } + should_email(merge_request.author) + should_email(@u_watcher) + should_email(@subscriber) + should_email(@watcher_and_subscriber) + should_email(@u_guest_watcher) + should_not_email(@u_participant_mentioned) + should_not_email(@u_guest_custom) + should_not_email(@u_custom_global) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) + should_not_email(@u_lazy_participant) + end + + it_behaves_like 'participating notifications' do + let(:participant) { create(:user, username: 'user-participant') } + let(:issuable) { merge_request } + let(:notification_trigger) { notification.change_in_merge_request_draft_status(merge_request, @u_disabled) } + end + + it_behaves_like 'project emails are disabled' do + let(:notification_target) { merge_request } + let(:notification_trigger) { notification.change_in_merge_request_draft_status(merge_request, @u_disabled) } + end + end + describe '#push_to_merge_request' do before do update_custom_notification(:push_to_merge_request, @u_guest_custom, resource: project) diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index e7bc1450601..b0d7274269b 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -18,12 +18,12 @@ RSpec.shared_context 'GroupPolicy context' do ] end - let(:read_group_permissions) { %i[read_label read_list read_milestone read_board] } + let(:read_group_permissions) { %i[read_label read_issue_board_list read_milestone read_issue_board] } let(:reporter_permissions) do %i[ admin_label - admin_board + admin_issue_board read_container_image read_metrics_dashboard_annotation read_prometheus diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb index 3016494ac8d..266c8d5ee84 100644 --- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb @@ -16,8 +16,8 @@ RSpec.shared_context 'ProjectPolicy context' do let(:base_guest_permissions) do %i[ award_emoji create_issue create_merge_request_in create_note - create_project read_board read_issue read_issue_iid read_issue_link - read_label read_list read_milestone read_note read_project + create_project read_issue_board read_issue read_issue_iid read_issue_link + read_label read_issue_board_list read_milestone read_note read_project read_project_for_iids read_project_member read_release read_snippet read_wiki upload_file ] @@ -25,7 +25,7 @@ RSpec.shared_context 'ProjectPolicy context' do let(:base_reporter_permissions) do %i[ - admin_issue admin_issue_link admin_label admin_list create_snippet + admin_issue admin_issue_link admin_label admin_issue_board_list create_snippet download_code download_wiki_code fork_project metrics_dashboard read_build read_commit_status read_confidential_issues read_container_image read_deployment read_environment read_merge_request diff --git a/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb index 54f4ba7ff73..274516cd87b 100644 --- a/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb @@ -25,7 +25,7 @@ RSpec.shared_examples 'group and project boards query' do board = create(:board, resource_parent: board_parent, name: 'A') allow(Ability).to receive(:allowed?).and_call_original - allow(Ability).to receive(:allowed?).with(user, :read_board, board).and_return(false) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, board).and_return(false) post_graphql(query, current_user: current_user)