diff options
Diffstat (limited to 'lib/gitlab/danger')
-rw-r--r-- | lib/gitlab/danger/helper.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/danger/roulette.rb | 85 | ||||
-rw-r--r-- | lib/gitlab/danger/teammate.rb | 13 |
3 files changed, 65 insertions, 35 deletions
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb index b8a27e5448a..0410fe8657d 100644 --- a/lib/gitlab/danger/helper.rb +++ b/lib/gitlab/danger/helper.rb @@ -53,7 +53,7 @@ module Gitlab def ee? # Support former project name for `dev` and support local Danger run - %w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?('../../ee') + %w[gitlab gitlab-ee].include?(ENV['CI_PROJECT_NAME']) || Dir.exist?(File.expand_path('../../../ee', __dir__)) end def gitlab_helper diff --git a/lib/gitlab/danger/roulette.rb b/lib/gitlab/danger/roulette.rb index ed4af3f4a43..2e6181d1cab 100644 --- a/lib/gitlab/danger/roulette.rb +++ b/lib/gitlab/danger/roulette.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative 'teammate' +require_relative 'request_helper' module Gitlab module Danger @@ -12,45 +13,49 @@ module Gitlab database: false }.freeze - Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role) + Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role, :timezone_experiment) + + def team_mr_author + team.find { |person| person.username == mr_author_username } + end # Assigns GitLab team members to be reviewer and maintainer # for each change category that a Merge Request contains. # # @return [Array<Spin>] - def spin(project, categories, branch_name, timezone_experiment: false) - team = - begin - project_team(project) - rescue => err - warn("Reviewer roulette failed to load team data: #{err.message}") - [] - end - - canonical_branch_name = canonical_branch_name(branch_name) - - spin_per_category = categories.each_with_object({}) do |category, memo| + def spin(project, categories, timezone_experiment: false) + spins = categories.map do |category| including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(category, timezone_experiment) - memo[category] = spin_for_category(team, project, category, canonical_branch_name, timezone_experiment: including_timezone) + spin_for_category(project, category, timezone_experiment: including_timezone) end - spin_per_category.map do |category, spin| - case category + backend_spin = spins.find { |spin| spin.category == :backend } + + spins.each do |spin| + including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(spin.category, timezone_experiment) + case spin.category + when :qa + # MR includes QA changes, but also other changes, and author isn't an SET + if categories.size > 1 && !team_mr_author&.reviewer?(project, spin.category, []) + spin.optional_role = :maintainer + end when :test + spin.optional_role = :maintainer + if spin.reviewer.nil? # Fetch an already picked backend reviewer, or pick one otherwise - spin.reviewer = spin_per_category[:backend]&.reviewer || spin_for_category(team, project, :backend, canonical_branch_name).reviewer + spin.reviewer = backend_spin&.reviewer || spin_for_category(project, :backend, timezone_experiment: including_timezone).reviewer end when :engineering_productivity if spin.maintainer.nil? # Fetch an already picked backend maintainer, or pick one otherwise - spin.maintainer = spin_per_category[:backend]&.maintainer || spin_for_category(team, project, :backend, canonical_branch_name).maintainer + spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer end end - - spin end + + spins end # Looks up the current list of GitLab team members and parses it into a @@ -73,14 +78,9 @@ module Gitlab # @return [Array<Teammate>] def project_team(project_name) team.select { |member| member.in_project?(project_name) } - end - - def canonical_branch_name(branch_name) - branch_name.gsub(/^[ce]e-|-[ce]e$/, '') - end - - def new_random(seed) - Random.new(Digest::MD5.hexdigest(seed).to_i(16)) + rescue => err + warn("Reviewer roulette failed to load team data: #{err.message}") + [] end # Known issue: If someone is rejected due to OOO, and then becomes not OOO, the @@ -113,16 +113,35 @@ module Gitlab # @param [Teammate] person # @return [Boolean] def mr_author?(person) - person.username == gitlab.mr_author + person.username == mr_author_username + end + + def mr_author_username + helper.gitlab_helper&.mr_author || `whoami` + end + + def mr_source_branch + return `git rev-parse --abbrev-ref HEAD` unless helper.gitlab_helper&.mr_json + + helper.gitlab_helper.mr_json['source_branch'] + end + + def mr_labels + helper.gitlab_helper&.mr_labels || [] + end + + def new_random(seed) + Random.new(Digest::MD5.hexdigest(seed).to_i(16)) end def spin_role_for_category(team, role, project, category) team.select do |member| - member.public_send("#{role}?", project, category, gitlab.mr_labels) # rubocop:disable GitlabSecurity/PublicSend + member.public_send("#{role}?", project, category, mr_labels) # rubocop:disable GitlabSecurity/PublicSend end end - def spin_for_category(team, project, category, branch_name, timezone_experiment: false) + def spin_for_category(project, category, timezone_experiment: false) + team = project_team(project) reviewers, traintainers, maintainers = %i[reviewer traintainer maintainer].map do |role| spin_role_for_category(team, role, project, category) @@ -132,11 +151,11 @@ module Gitlab # https://gitlab.com/gitlab-org/gitlab/issues/26723 # Make traintainers have triple the chance to be picked as a reviewer - random = new_random(branch_name) + random = new_random(mr_source_branch) reviewer = spin_for_person(reviewers + traintainers + traintainers, random: random, timezone_experiment: timezone_experiment) maintainer = spin_for_person(maintainers, random: random, timezone_experiment: timezone_experiment) - Spin.new(category, reviewer, maintainer) + Spin.new(category, reviewer, maintainer, false, timezone_experiment) end end end diff --git a/lib/gitlab/danger/teammate.rb b/lib/gitlab/danger/teammate.rb index f7da66e77cd..a06c3554d3d 100644 --- a/lib/gitlab/danger/teammate.rb +++ b/lib/gitlab/danger/teammate.rb @@ -3,10 +3,11 @@ module Gitlab module Danger class Teammate - attr_reader :username, :name, :role, :projects, :available, :tz_offset_hours + attr_reader :options, :username, :name, :role, :projects, :available, :tz_offset_hours # The options data are produced by https://gitlab.com/gitlab-org/gitlab-roulette/-/blob/master/lib/team_member.rb def initialize(options = {}) + @options = options @username = options['username'] @name = options['name'] @markdown_name = options['markdown_name'] @@ -16,6 +17,16 @@ module Gitlab @tz_offset_hours = options['tz_offset_hours'] end + def to_h + options + end + + def ==(other) + return false unless other.respond_to?(:username) + + other.username == username + end + def in_project?(name) projects&.has_key?(name) end |