From f64a639bcfa1fc2bc89ca7db268f594306edfd7c Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 16 Mar 2021 18:18:33 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-10-stable-ee --- app/finders/admin/plans_finder.rb | 24 +++++ app/finders/boards/boards_finder.rb | 32 +++++++ .../ci/daily_build_group_report_results_finder.rb | 103 +++++++++++++++------ .../daily_build_group_report_results_finder.rb | 88 ------------------ app/finders/issuable_finder.rb | 52 +++++------ app/finders/issuable_finder/params.rb | 27 ------ app/finders/issuables/author_filter.rb | 41 ++++++++ app/finders/issuables/base_filter.rb | 36 +++++++ app/finders/merge_request_target_project_finder.rb | 11 ++- .../merge_requests/oldest_per_commit_finder.rb | 32 ++++++- app/finders/namespaces/projects_finder.rb | 64 +++++++++++++ app/finders/packages/maven/package_finder.rb | 5 +- app/finders/packages/npm/package_finder.rb | 37 +++++--- app/finders/projects/groups_finder.rb | 68 ++++++++++++++ .../repositories/changelog_commits_finder.rb | 101 ++++++++++++++++++++ .../repositories/commits_with_trailer_finder.rb | 82 ---------------- app/finders/repositories/previous_tag_finder.rb | 12 ++- .../security/license_compliance_jobs_finder.rb | 2 +- app/finders/template_finder.rb | 16 +++- app/finders/users_finder.rb | 9 +- 20 files changed, 557 insertions(+), 285 deletions(-) create mode 100644 app/finders/admin/plans_finder.rb create mode 100644 app/finders/boards/boards_finder.rb delete mode 100644 app/finders/ci/testing/daily_build_group_report_results_finder.rb create mode 100644 app/finders/issuables/author_filter.rb create mode 100644 app/finders/issuables/base_filter.rb create mode 100644 app/finders/namespaces/projects_finder.rb create mode 100644 app/finders/projects/groups_finder.rb create mode 100644 app/finders/repositories/changelog_commits_finder.rb delete mode 100644 app/finders/repositories/commits_with_trailer_finder.rb (limited to 'app/finders') diff --git a/app/finders/admin/plans_finder.rb b/app/finders/admin/plans_finder.rb new file mode 100644 index 00000000000..5ca4b61b25e --- /dev/null +++ b/app/finders/admin/plans_finder.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Admin + class PlansFinder + attr_reader :params + + def initialize(params = {}) + @params = params + end + + def execute + plans = Plan.all + by_name(plans) + end + + private + + def by_name(plans) + return plans unless params[:name] + + Plan.find_by(name: params[:name]) # rubocop: disable CodeReuse/ActiveRecord + end + end +end diff --git a/app/finders/boards/boards_finder.rb b/app/finders/boards/boards_finder.rb new file mode 100644 index 00000000000..5b8c313a178 --- /dev/null +++ b/app/finders/boards/boards_finder.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Boards + class BoardsFinder < Boards::BaseService + def execute + find_boards + end + + private + + def boards + parent.boards.order_by_name_asc + end + + def first_board + parent.boards.first_board + end + + def find_boards + found = + if parent.multiple_issue_boards_available? + boards + else + # When multiple issue boards are not available + # a user is only allowed to view the default shown board + first_board + end + + params[:board_id].present? ? [found.find(params[:board_id])] : found + end + end +end diff --git a/app/finders/ci/daily_build_group_report_results_finder.rb b/app/finders/ci/daily_build_group_report_results_finder.rb index ef97ccb4c0f..9e736c70dda 100644 --- a/app/finders/ci/daily_build_group_report_results_finder.rb +++ b/app/finders/ci/daily_build_group_report_results_finder.rb @@ -1,56 +1,99 @@ # frozen_string_literal: true +# DailyBuildGroupReportResultsFinder +# +# Used to filter DailyBuildGroupReportResults by set of params +# +# Arguments: +# current_user +# params: +# project: integer +# group: integer +# coverage: boolean +# ref_path: string +# start_date: string +# end_date: string +# sort: boolean +# limit: integer + module Ci class DailyBuildGroupReportResultsFinder include Gitlab::Allowable - def initialize(current_user:, project:, ref_path: nil, start_date:, end_date:, limit: nil) + MAX_ITEMS = 1_000 + REPORT_WINDOW = 90.days + DATE_FORMAT_ALLOWED = '%Y-%m-%d' + + attr_reader :params, :current_user + + def initialize(params: {}, current_user: nil) + @params = params @current_user = current_user - @project = project - @ref_path = ref_path - @start_date = start_date - @end_date = end_date - @limit = limit end def execute - return none unless query_allowed? + return Ci::DailyBuildGroupReportResult.none unless query_allowed? - query + collection = Ci::DailyBuildGroupReportResult.by_projects(params[:project]) + collection = filter_report_results(collection) + collection end - protected + private - attr_reader :current_user, :project, :ref_path, :start_date, :end_date, :limit + def query_allowed? + can?(current_user, :read_build_report_results, params[:project]) + end - def query - Ci::DailyBuildGroupReportResult.recent_results( - query_params, - limit: limit - ) + def filter_report_results(collection) + collection = by_coverage(collection) + collection = by_ref_path(collection) + collection = by_dates(collection) + + collection = sort(collection) + collection = limit_by(collection) + collection end - def query_allowed? - can?(current_user, :read_build_report_results, project) + def by_coverage(items) + params[:coverage].present? ? items.with_coverage : items + end + + def by_ref_path(items) + params[:ref_path].present? ? items.by_ref_path(params[:ref_path]) : items.with_default_branch end - def query_params - params = { - project_id: project, - date: start_date..end_date - } + def by_dates(items) + params[:start_date].present? && params[:end_date].present? ? items.by_dates(start_date, end_date) : items + end - if ref_path.present? - params[:ref_path] = ref_path - else - params[:default_branch] = true - end + def sort(items) + params[:sort].present? ? items.ordered_by_date_and_group_name : items + end - params + # rubocop: disable CodeReuse/ActiveRecord + def limit_by(items) + items.limit(limit) end + # rubocop: enable CodeReuse/ActiveRecord + + def limit + return MAX_ITEMS unless params[:limit].present? - def none - Ci::DailyBuildGroupReportResult.none + [params[:limit].to_i, MAX_ITEMS].min + end + + def start_date + start_date = Date.strptime(params[:start_date], DATE_FORMAT_ALLOWED) rescue REPORT_WINDOW.ago.to_date + + # The start_date cannot be older than `end_date - 90 days` + [start_date, end_date - REPORT_WINDOW].max + end + + def end_date + Date.strptime(params[:end_date], DATE_FORMAT_ALLOWED) rescue Date.current end end end + +Ci::DailyBuildGroupReportResultsFinder.prepend_if_ee('::EE::Ci::DailyBuildGroupReportResultsFinder') diff --git a/app/finders/ci/testing/daily_build_group_report_results_finder.rb b/app/finders/ci/testing/daily_build_group_report_results_finder.rb deleted file mode 100644 index 70d9e55dc47..00000000000 --- a/app/finders/ci/testing/daily_build_group_report_results_finder.rb +++ /dev/null @@ -1,88 +0,0 @@ -# frozen_string_literal: true - -# DailyBuildGroupReportResultsFinder -# -# Used to filter DailyBuildGroupReportResults by set of params -# -# Arguments: -# current_user -# params: -# project: integer -# group: integer -# coverage: boolean -# ref_path: string -# start_date: date -# end_date: date -# sort: boolean -# limit: integer - -module Ci - module Testing - class DailyBuildGroupReportResultsFinder - include Gitlab::Allowable - - MAX_ITEMS = 1_000 - - attr_reader :params, :current_user - - def initialize(params: {}, current_user: nil) - @params = params - @current_user = current_user - end - - def execute - return Ci::DailyBuildGroupReportResult.none unless query_allowed? - - collection = Ci::DailyBuildGroupReportResult.by_projects(params[:project]) - collection = filter_report_results(collection) - collection - end - - private - - def query_allowed? - can?(current_user, :read_build_report_results, params[:project]) - end - - def filter_report_results(collection) - collection = by_coverage(collection) - collection = by_ref_path(collection) - collection = by_dates(collection) - - collection = sort(collection) - collection = limit_by(collection) - collection - end - - def by_coverage(items) - params[:coverage].present? ? items.with_coverage : items - end - - def by_ref_path(items) - params[:ref_path].present? ? items.by_ref_path(params[:ref_path]) : items.with_default_branch - end - - def by_dates(items) - params[:start_date].present? && params[:end_date].present? ? items.by_dates(params[:start_date], params[:end_date]) : items - end - - def sort(items) - params[:sort].present? ? items.ordered_by_date_and_group_name : items - end - - # rubocop: disable CodeReuse/ActiveRecord - def limit_by(items) - items.limit(limit) - end - # rubocop: enable CodeReuse/ActiveRecord - - def limit - return MAX_ITEMS unless params[:limit].present? - - [params[:limit].to_i, MAX_ITEMS].min - end - end - end -end - -Ci::Testing::DailyBuildGroupReportResultsFinder.prepend_if_ee('::EE::Ci::Testing::DailyBuildGroupReportResultsFinder') diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index fc03d5cd90c..2409dc9d77d 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -47,6 +47,7 @@ class IssuableFinder NEGATABLE_PARAMS_HELPER_KEYS = %i[project_id scope status include_subgroups].freeze attr_accessor :current_user, :params + attr_reader :original_params attr_writer :parent delegate(*%i[assignee milestones], to: :params) @@ -87,7 +88,7 @@ class IssuableFinder end def valid_params - @valid_params ||= scalar_params + [array_params.merge(not: {})] + @valid_params ||= scalar_params + [array_params.merge(or: {}, not: {})] end end @@ -101,6 +102,7 @@ class IssuableFinder def initialize(current_user, params = {}) @current_user = current_user + @original_params = params @params = params_class.new(params, current_user, klass) end @@ -142,7 +144,7 @@ class IssuableFinder end def should_filter_negated_args? - return false unless Feature.enabled?(:not_issuable_queries, params.group || params.project, default_enabled: true) + return false unless not_filters_enabled? # API endpoints send in `nil` values so we test if there are any non-nil not_params.present? && not_params.values.any? @@ -150,7 +152,6 @@ class IssuableFinder # Negates all params found in `negatable_params` def filter_negated_items(items) - items = by_negated_author(items) items = by_negated_assignee(items) items = by_negated_label(items) items = by_negated_milestone(items) @@ -372,31 +373,14 @@ class IssuableFinder end # rubocop: enable CodeReuse/ActiveRecord - # rubocop: disable CodeReuse/ActiveRecord def by_author(items) - if params.author - items.where(author_id: params.author.id) - elsif params.no_author? - items.where(author_id: nil) - elsif params.author_id? || params.author_username? # author not found - items.none - else - items - end - end - # rubocop: enable CodeReuse/ActiveRecord - - # rubocop: disable CodeReuse/ActiveRecord - def by_negated_author(items) - if not_params.author - items.where.not(author_id: not_params.author.id) - elsif not_params.author_id? || not_params.author_username? # author not found - items.none - else - items - end + Issuables::AuthorFilter.new( + items, + params: original_params, + or_filters_enabled: or_filters_enabled?, + not_filters_enabled: not_filters_enabled? + ).filter end - # rubocop: enable CodeReuse/ActiveRecord def by_assignee(items) if params.filter_by_no_assignee? @@ -514,4 +498,20 @@ class IssuableFinder def by_non_archived(items) params[:non_archived].present? ? items.non_archived : items end + + def or_filters_enabled? + strong_memoize(:or_filters_enabled) do + Feature.enabled?(:or_issuable_queries, feature_flag_scope, default_enabled: :yaml) + end + end + + def not_filters_enabled? + strong_memoize(:not_filters_enabled) do + Feature.enabled?(:not_issuable_queries, feature_flag_scope, default_enabled: :yaml) + end + end + + def feature_flag_scope + params.group || params.project + end end diff --git a/app/finders/issuable_finder/params.rb b/app/finders/issuable_finder/params.rb index 803b30e86ac..a62210ceac5 100644 --- a/app/finders/issuable_finder/params.rb +++ b/app/finders/issuable_finder/params.rb @@ -27,19 +27,6 @@ class IssuableFinder params.present? end - def author_id? - params[:author_id].present? && params[:author_id] != NONE - end - - def author_username? - params[:author_username].present? && params[:author_username] != NONE - end - - def no_author? - # author_id takes precedence over author_username - params[:author_id] == NONE || params[:author_username] == NONE - end - def filter_by_no_assignee? params[:assignee_id].to_s.downcase == FILTER_NONE end @@ -169,20 +156,6 @@ class IssuableFinder end end - # rubocop: disable CodeReuse/ActiveRecord - def author - strong_memoize(:author) do - if author_id? - User.find_by(id: params[:author_id]) - elsif author_username? - User.find_by_username(params[:author_username]) - else - nil - end - end - end - # rubocop: enable CodeReuse/ActiveRecord - # rubocop: disable CodeReuse/ActiveRecord def assignees strong_memoize(:assignees) do diff --git a/app/finders/issuables/author_filter.rb b/app/finders/issuables/author_filter.rb new file mode 100644 index 00000000000..ce68dbafb95 --- /dev/null +++ b/app/finders/issuables/author_filter.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Issuables + class AuthorFilter < BaseFilter + def filter + filtered = by_author(issuables) + filtered = by_author_union(filtered) + by_negated_author(filtered) + end + + private + + def by_author(issuables) + if params[:author_id].present? + issuables.authored(params[:author_id]) + elsif params[:author_username].present? + issuables.authored(User.by_username(params[:author_username])) + else + issuables + end + end + + def by_author_union(issuables) + return issuables unless or_filters_enabled? && or_params&.fetch(:author_username).present? + + issuables.authored(User.by_username(or_params[:author_username])) + end + + def by_negated_author(issuables) + return issuables unless not_filters_enabled? && not_params.present? + + if not_params[:author_id].present? + issuables.not_authored(not_params[:author_id]) + elsif not_params[:author_username].present? + issuables.not_authored(User.by_username(not_params[:author_username])) + else + issuables + end + end + end +end diff --git a/app/finders/issuables/base_filter.rb b/app/finders/issuables/base_filter.rb new file mode 100644 index 00000000000..641ae2568cc --- /dev/null +++ b/app/finders/issuables/base_filter.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Issuables + class BaseFilter + attr_reader :issuables, :params + + def initialize(issuables, params:, or_filters_enabled: false, not_filters_enabled: false) + @issuables = issuables + @params = params + @or_filters_enabled = or_filters_enabled + @not_filters_enabled = not_filters_enabled + end + + def filter + raise NotImplementedError + end + + private + + def or_params + params[:or] + end + + def not_params + params[:not] + end + + def or_filters_enabled? + @or_filters_enabled + end + + def not_filters_enabled? + @not_filters_enabled + end + end +end diff --git a/app/finders/merge_request_target_project_finder.rb b/app/finders/merge_request_target_project_finder.rb index 85a73e0c6ff..dc9b28ab0a0 100644 --- a/app/finders/merge_request_target_project_finder.rb +++ b/app/finders/merge_request_target_project_finder.rb @@ -5,29 +5,30 @@ class MergeRequestTargetProjectFinder attr_reader :current_user, :source_project - def initialize(current_user: nil, source_project:) + def initialize(current_user: nil, source_project:, project_feature: :merge_requests) @current_user = current_user @source_project = source_project + @project_feature = project_feature end - # rubocop: disable CodeReuse/ActiveRecord def execute(include_routes: false) if source_project.fork_network include_routes ? projects.inc_routes : projects else - Project.where(id: source_project) + Project.id_in(source_project.id) end end - # rubocop: enable CodeReuse/ActiveRecord private + attr_reader :project_feature + def projects source_project .fork_network .projects .public_or_visible_to_user(current_user) .non_archived - .with_feature_available_for_user(:merge_requests, current_user) + .with_feature_available_for_user(project_feature, current_user) end end diff --git a/app/finders/merge_requests/oldest_per_commit_finder.rb b/app/finders/merge_requests/oldest_per_commit_finder.rb index f50db43d7d2..5360f301036 100644 --- a/app/finders/merge_requests/oldest_per_commit_finder.rb +++ b/app/finders/merge_requests/oldest_per_commit_finder.rb @@ -15,19 +15,45 @@ module MergeRequests # Returns a Hash that maps a commit ID to the oldest merge request that # introduced that commit. def execute(commits) + mapping = {} + shas = commits.map(&:id) + + # To include merge requests by the commit SHA, we don't need to go through + # any diff rows. + # + # We can't squeeze all this into a single query, as the diff based data + # relies on a GROUP BY. On the other hand, retrieving MRs by their merge + # SHAs separately is much easier, and plenty fast. + @project + .merge_requests + .preload_target_project + .by_merge_commit_sha(shas) + .each do |mr| + # Merge SHAs can't be in the merge request itself. It _is_ possible a + # newer merge request includes the merge commit, but in that case we + # still want the oldest merge request. + mapping[mr.merge_commit_sha] = mr + end + + remaining = shas - mapping.keys + + return mapping if remaining.empty? + id_rows = MergeRequestDiffCommit - .oldest_merge_request_id_per_commit(@project.id, commits.map(&:id)) + .oldest_merge_request_id_per_commit(@project.id, remaining) mrs = MergeRequest .preload_target_project .id_in(id_rows.map { |r| r[:merge_request_id] }) .index_by(&:id) - id_rows.each_with_object({}) do |row, hash| + id_rows.each do |row| if (mr = mrs[row[:merge_request_id]]) - hash[row[:sha]] = mr + mapping[row[:sha]] = mr end end + + mapping end end end diff --git a/app/finders/namespaces/projects_finder.rb b/app/finders/namespaces/projects_finder.rb new file mode 100644 index 00000000000..a6d98015e9d --- /dev/null +++ b/app/finders/namespaces/projects_finder.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# Namespaces::ProjectsFinder +# +# Used to filter Projects by set of params +# +# Arguments: +# current_user +# namespace +# params: +# sort: string +# search: string +# include_subgroups: boolean +# ids: int[] +# +module Namespaces + class ProjectsFinder + def initialize(namespace: nil, current_user: nil, params: {}) + @namespace = namespace + @current_user = current_user + @params = params + end + + def execute + return Project.none if namespace.nil? + + collection = if params[:include_subgroups].present? + namespace.all_projects.with_route + else + namespace.projects.with_route + end + + filter_projects(collection) + end + + private + + attr_reader :namespace, :params, :current_user + + def filter_projects(collection) + collection = by_ids(collection) + collection = by_similarity(collection) + collection + end + + def by_ids(items) + return items unless params[:ids].present? + + items.id_in(params[:ids]) + end + + def by_similarity(items) + return items unless params[:search].present? + + if params[:sort] == :similarity + items = items.sorted_by_similarity_desc(params[:search], include_in_select: true) + end + + items.merge(Project.search(params[:search])) + end + end +end + +Namespaces::ProjectsFinder.prepend_if_ee('::EE::Namespaces::ProjectsFinder') diff --git a/app/finders/packages/maven/package_finder.rb b/app/finders/packages/maven/package_finder.rb index 7e753705cbd..ba3d4631f55 100644 --- a/app/finders/packages/maven/package_finder.rb +++ b/app/finders/packages/maven/package_finder.rb @@ -59,9 +59,8 @@ module Packages # Returns the projects that the current user can view within a group. def projects_visible_to_current_user - ::Project - .in_namespace(group.self_and_descendants.select(:id)) - .public_or_visible_to_user(current_user) + group.all_projects + .public_or_visible_to_user(current_user) end end end diff --git a/app/finders/packages/npm/package_finder.rb b/app/finders/packages/npm/package_finder.rb index 2854226e178..3b79785d0e1 100644 --- a/app/finders/packages/npm/package_finder.rb +++ b/app/finders/packages/npm/package_finder.rb @@ -2,29 +2,40 @@ module Packages module Npm class PackageFinder - attr_reader :project, :package_name - delegate :find_by_version, to: :execute + delegate :last, to: :execute - def initialize(project, package_name) - @project = project + def initialize(package_name, project: nil, namespace: nil) @package_name = package_name + @project = project + @namespace = namespace end def execute - return Packages::Package.none unless project - - packages + base.npm + .with_name(@package_name) + .last_of_each_version + .preload_files end private - def packages - project.packages - .npm - .with_name(package_name) - .last_of_each_version - .preload_files + def base + if @project + packages_for_project + elsif @namespace + packages_for_namespace + else + ::Packages::Package.none + end + end + + def packages_for_project + @project.packages + end + + def packages_for_namespace + ::Packages::Package.for_projects(@namespace.all_projects) end end end diff --git a/app/finders/projects/groups_finder.rb b/app/finders/projects/groups_finder.rb new file mode 100644 index 00000000000..d0c42ad5611 --- /dev/null +++ b/app/finders/projects/groups_finder.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +# Used to filter ancestor and shared project's Groups by a set of params +# +# Arguments: +# project +# current_user - which user is requesting groups +# params: +# with_shared: boolean (optional) +# shared_min_access_level: integer (optional) +# skip_groups: array of integers (optional) +# +module Projects + class GroupsFinder < UnionFinder + def initialize(project:, current_user: nil, params: {}) + @project = project + @current_user = current_user + @params = params + end + + def execute + return Group.none unless authorized? + + items = all_groups.map do |item| + item = exclude_group_ids(item) + item + end + + find_union(items, Group).with_route.order_id_desc + end + + private + + attr_reader :project, :current_user, :params + + def authorized? + Ability.allowed?(current_user, :read_project, project) + end + + # rubocop: disable CodeReuse/ActiveRecord + def all_groups + groups = [] + groups << project.group.self_and_ancestors if project.group + + if params[:with_shared] + shared_groups = project.invited_groups + + if params[:shared_min_access_level] + shared_groups = shared_groups.where( + 'project_group_links.group_access >= ?', params[:shared_min_access_level] + ) + end + + groups << shared_groups + end + + groups << Group.none if groups.compact.empty? + groups + end + # rubocop: enable CodeReuse/ActiveRecord + + def exclude_group_ids(groups) + return groups unless params[:skip_groups] + + groups.id_not_in(params[:skip_groups]) + end + end +end diff --git a/app/finders/repositories/changelog_commits_finder.rb b/app/finders/repositories/changelog_commits_finder.rb new file mode 100644 index 00000000000..b80b8e94e59 --- /dev/null +++ b/app/finders/repositories/changelog_commits_finder.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Repositories + # Finder for getting the commits to include in a changelog. + class ChangelogCommitsFinder + # The maximum number of commits to retrieve per page. + # + # This value is arbitrarily chosen. Lowering it means more Gitaly calls, but + # less data being loaded into memory at once. Increasing it has the opposite + # effect. + # + # This amount is based around the number of commits that usually go in a + # GitLab release. Some examples for GitLab's own releases: + # + # * 13.6.0: 4636 commits + # * 13.5.0: 5912 commits + # * 13.4.0: 5541 commits + # + # Using this limit should result in most (very large) projects only needing + # 5-10 Gitaly calls, while keeping memory usage at a reasonable amount. + COMMITS_PER_PAGE = 1024 + + # The regex to use for extracting the SHA of a reverted commit. + REVERT_REGEX = /^This reverts commit (?[0-9a-f]{40})/i.freeze + + # The `project` argument specifies the project for which to obtain the + # commits. + # + # The `from` and `to` arguments specify the range of commits to include. The + # commit specified in `from` won't be included itself. The commit specified + # in `to` _is_ included. + # + # The `per_page` argument specifies how many commits are retrieved in a single + # Gitaly API call. + def initialize(project:, from:, to:, per_page: COMMITS_PER_PAGE) + @project = project + @from = from + @to = to + @per_page = per_page + end + + # Fetches all commits that have the given trailer set. + # + # The commits are yielded to the supplied block in batches. This allows + # other code to process these commits in batches too, instead of first + # having to load all commits into memory. + # + # Example: + # + # ChangelogCommitsFinder.new(...).each_page('Changelog') do |commits| + # commits.each do |commit| + # ... + # end + # end + def each_page(trailer) + return to_enum(__method__, trailer) unless block_given? + + offset = 0 + reverted = Set.new + response = fetch_commits + + while response.any? + commits = [] + + response.each do |commit| + # If the commit is reverted in the same range (by a newer commit), we + # won't include it. This works here because commits are processed in + # reverse order (= newer first). + next if reverted.include?(commit.id) + + if (sha = revert_commit_sha(commit)) + reverted << sha + end + + commits.push(commit) if commit.trailers.key?(trailer) + end + + yield commits + + offset += response.length + response = fetch_commits(offset) + end + end + + private + + def fetch_commits(offset = 0) + range = "#{@from}..#{@to}" + + @project + .repository + .commits(range, limit: @per_page, offset: offset, trailers: true) + end + + def revert_commit_sha(commit) + matches = commit.description&.match(REVERT_REGEX) + + matches[:sha] if matches + end + end +end diff --git a/app/finders/repositories/commits_with_trailer_finder.rb b/app/finders/repositories/commits_with_trailer_finder.rb deleted file mode 100644 index 4bd643c345b..00000000000 --- a/app/finders/repositories/commits_with_trailer_finder.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -module Repositories - # Finder for obtaining commits between two refs, with a Git trailer set. - class CommitsWithTrailerFinder - # The maximum number of commits to retrieve per page. - # - # This value is arbitrarily chosen. Lowering it means more Gitaly calls, but - # less data being loaded into memory at once. Increasing it has the opposite - # effect. - # - # This amount is based around the number of commits that usually go in a - # GitLab release. Some examples for GitLab's own releases: - # - # * 13.6.0: 4636 commits - # * 13.5.0: 5912 commits - # * 13.4.0: 5541 commits - # - # Using this limit should result in most (very large) projects only needing - # 5-10 Gitaly calls, while keeping memory usage at a reasonable amount. - COMMITS_PER_PAGE = 1024 - - # The `project` argument specifies the project for which to obtain the - # commits. - # - # The `from` and `to` arguments specify the range of commits to include. The - # commit specified in `from` won't be included itself. The commit specified - # in `to` _is_ included. - # - # The `per_page` argument specifies how many commits are retrieved in a single - # Gitaly API call. - def initialize(project:, from:, to:, per_page: COMMITS_PER_PAGE) - @project = project - @from = from - @to = to - @per_page = per_page - end - - # Fetches all commits that have the given trailer set. - # - # The commits are yielded to the supplied block in batches. This allows - # other code to process these commits in batches too, instead of first - # having to load all commits into memory. - # - # Example: - # - # CommitsWithTrailerFinder.new(...).each_page('Signed-off-by') do |commits| - # commits.each do |commit| - # ... - # end - # end - def each_page(trailer) - return to_enum(__method__, trailer) unless block_given? - - offset = 0 - response = fetch_commits - - while response.any? - commits = [] - - response.each do |commit| - commits.push(commit) if commit.trailers.key?(trailer) - end - - yield commits - - offset += response.length - response = fetch_commits(offset) - end - end - - private - - def fetch_commits(offset = 0) - range = "#{@from}..#{@to}" - - @project - .repository - .commits(range, limit: @per_page, offset: offset, trailers: true) - end - end -end diff --git a/app/finders/repositories/previous_tag_finder.rb b/app/finders/repositories/previous_tag_finder.rb index 150a6332c29..b5e786c30e9 100644 --- a/app/finders/repositories/previous_tag_finder.rb +++ b/app/finders/repositories/previous_tag_finder.rb @@ -16,12 +16,13 @@ module Repositories # This finder expects that all tags to consider meet the following # requirements: # - # * They start with the letter "v" - # * They use semantic versioning for the tag format + # * They start with the letter "v" followed by a version, or immediately start + # with a version + # * They use semantic versioning for the version format # # Tags not meeting these requirements are ignored. class PreviousTagFinder - TAG_REGEX = /\Av(?#{Gitlab::Regex.unbounded_semver_regex})\z/.freeze + TAG_REGEX = /\Av?(?#{Gitlab::Regex.unbounded_semver_regex})\z/.freeze def initialize(project) @project = project @@ -36,6 +37,11 @@ module Repositories next unless matches + # When using this class for generating changelog data for a range of + # commits, we want to compare against the tag of the last _stable_ + # release; not some random RC that came after that. + next if matches[:prerelease] + version = matches[:version] tags[version] = tag versions << version diff --git a/app/finders/security/license_compliance_jobs_finder.rb b/app/finders/security/license_compliance_jobs_finder.rb index 100f94b2cc7..0ec428ae408 100644 --- a/app/finders/security/license_compliance_jobs_finder.rb +++ b/app/finders/security/license_compliance_jobs_finder.rb @@ -12,7 +12,7 @@ module Security class LicenseComplianceJobsFinder < JobsFinder def self.allowed_job_types - [:license_management, :license_scanning] + [:license_scanning] end end end diff --git a/app/finders/template_finder.rb b/app/finders/template_finder.rb index 36f8d144908..739beee236c 100644 --- a/app/finders/template_finder.rb +++ b/app/finders/template_finder.rb @@ -22,16 +22,26 @@ class TemplateFinder end end + # This is temporary and will be removed once we introduce group level inherited templates and + # remove the inherited_issuable_templates FF + def all_template_names_hash_or_array(project, issuable_type) + if project.inherited_issuable_templates_enabled? + all_template_names(project, issuable_type.pluralize) + else + all_template_names_array(project, issuable_type.pluralize) + end + end + def all_template_names(project, type) return {} if !VENDORED_TEMPLATES.key?(type.to_s) && type.to_s != 'licenses' build(type, project).template_names end - # This is issues and merge requests description templates only. - # This will be removed once we introduce group level inherited templates + # This is for issues and merge requests description templates only. + # This will be removed once we introduce group level inherited templates and remove the inherited_issuable_templates FF def all_template_names_array(project, type) - all_template_names(project, type).values.flatten.uniq + all_template_names(project, type).values.flatten.select { |tmpl| tmpl[:project_id] == project.id }.compact.uniq end end diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb index 42042406f3f..5ac905e0dd4 100644 --- a/app/finders/users_finder.rb +++ b/app/finders/users_finder.rb @@ -14,6 +14,7 @@ # active: boolean # blocked: boolean # external: boolean +# non_external: boolean # without_projects: boolean # sort: string # id: integer @@ -40,6 +41,7 @@ class UsersFinder users = by_active(users) users = by_external_identity(users) users = by_external(users) + users = by_non_external(users) users = by_2fa(users) users = by_created_at(users) users = by_without_projects(users) @@ -97,13 +99,18 @@ class UsersFinder # rubocop: disable CodeReuse/ActiveRecord def by_external(users) - return users = users.where.not(external: true) unless current_user&.admin? return users unless params[:external] users.external end # rubocop: enable CodeReuse/ActiveRecord + def by_non_external(users) + return users unless params[:non_external] + + users.non_external + end + def by_2fa(users) case params[:two_factor] when 'enabled' -- cgit v1.2.3