lib/danger/plugins/roulette.rb in gitlab-dangerfiles-1.1.1 vs lib/danger/plugins/roulette.rb in gitlab-dangerfiles-2.0.0
- old
+ new
@@ -14,27 +14,36 @@
INCLUDE_TIMEZONE_FOR_CATEGORY = {
database: false,
}.freeze
Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role, :timezone_experiment)
+ HTTPError = Class.new(StandardError)
+ # Finds the +Gitlab::Dangerfiles::Teammate+ object whose username matches the MR author username.
+ #
+ # @return [Gitlab::Dangerfiles::Teammate]
def team_mr_author
- team.find { |person| person.username == helper.mr_author }
+ company_members.find { |person| person.username == helper.mr_author }
end
# Assigns GitLab team members to be reviewer and maintainer
- # for each change category that a Merge Request contains.
+ # for the given +categories+.
#
+ # @param project [String] A project path.
+ # @param categories [Array<Symbol>] An array of categories symbols.
+ # @param timezone_experiment [Boolean] Whether to select reviewers based in timezone or not.
+ #
# @return [Array<Spin>]
def spin(project, categories = [nil], timezone_experiment: false)
spins = categories.sort.map do |category|
including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(category, timezone_experiment)
spin_for_category(project, category, timezone_experiment: including_timezone)
end
backend_spin = spins.find { |spin| spin.category == :backend }
+ frontend_spin = spins.find { |spin| spin.category == :frontend }
spins.each do |spin|
including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(spin.category, timezone_experiment)
case spin.category
when :qa
@@ -59,68 +68,36 @@
# Fetch an already picked backend maintainer, or pick one otherwise
spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
end
when :product_intelligence
spin.optional_role = :maintainer
+
+ if spin.maintainer.nil?
+ # Fetch an already picked maintainer, or pick one otherwise
+ spin.maintainer = backend_spin&.maintainer || frontend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
+ end
end
end
spins
end
- # Looks up the current list of GitLab team members and parses it into a
- # useful form
- #
- # @return [Array<Teammate>]
- def team
- @team ||= begin
- data = helper.http_get_json(ROULETTE_DATA_URL)
- data.map { |hash| Gitlab::Dangerfiles::Teammate.new(hash) }
- rescue JSON::ParserError
- raise "Failed to parse JSON response from #{ROULETTE_DATA_URL}"
- end
- end
-
- # Like +team+, but only returns teammates in the current project, based on
- # project_name.
- #
- # @return [Array<Teammate>]
- def project_team(project_name)
- team.select { |member| member.in_project?(project_name) }
- 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
- # selection will change on next spin
- # @param [Array<Teammate>] people
- def spin_for_person(people, random:, timezone_experiment: false)
- shuffled_people = people.shuffle(random: random)
-
- if timezone_experiment
- shuffled_people.find(&method(:valid_person_with_timezone?))
- else
- shuffled_people.find(&method(:valid_person?))
- end
- end
-
private
- # @param [Teammate] person
+ # @param [Gitlab::Dangerfiles::Teammate] person
# @return [Boolean]
def valid_person?(person)
!mr_author?(person) && person.available
end
- # @param [Teammate] person
+ # @param [Gitlab::Dangerfiles::Teammate] person
# @return [Boolean]
def valid_person_with_timezone?(person)
valid_person?(person) && HOURS_WHEN_PERSON_CAN_BE_PICKED.cover?(person.local_hour)
end
- # @param [Teammate] person
+ # @param [Gitlab::Dangerfiles::Teammate] person
# @return [Boolean]
def mr_author?(person)
person.username == helper.mr_author
end
@@ -132,10 +109,26 @@
team.select do |member|
member.public_send("#{role}?", project, category, helper.mr_labels) # rubocop:disable GitlabSecurity/PublicSend
end
end
+ # Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
+ # selection will change on next spin.
+ #
+ # @param [Array<Gitlab::Dangerfiles::Teammate>] people
+ #
+ # @return [Gitlab::Dangerfiles::Teammate]
+ def spin_for_person(people, random:, timezone_experiment: false)
+ shuffled_people = people.shuffle(random: random)
+
+ if timezone_experiment
+ shuffled_people.find(&method(:valid_person_with_timezone?))
+ else
+ shuffled_people.find(&method(:valid_person?))
+ end
+ end
+
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)
@@ -148,8 +141,47 @@
reviewer = spin_for_person(weighted_reviewers, random: random, timezone_experiment: timezone_experiment)
maintainer = spin_for_person(weighted_maintainers, random: random, timezone_experiment: timezone_experiment)
Spin.new(category, reviewer, maintainer, false, timezone_experiment)
+ end
+
+ # Fetches the given +url+ and parse its response as JSON.
+ #
+ # @param [String] url
+ #
+ # @return [Hash, Array]
+ def http_get_json(url)
+ rsp = Net::HTTP.get_response(URI.parse(url))
+
+ unless rsp.is_a?(Net::HTTPOK)
+ raise HTTPError, "Failed to read #{url}: #{rsp.code} #{rsp.message}"
+ end
+
+ JSON.parse(rsp.body)
+ end
+
+ # Looks up the current list of GitLab team members and parses it into a
+ # useful form.
+ #
+ # @return [Array<Gitlab::Dangerfiles::Teammate>]
+ def company_members
+ @company_members ||= begin
+ data = http_get_json(ROULETTE_DATA_URL)
+ data.map { |hash| Gitlab::Dangerfiles::Teammate.new(hash) }
+ rescue JSON::ParserError
+ raise "Failed to parse JSON response from #{ROULETTE_DATA_URL}"
+ end
+ end
+
+ # Like +team+, but only returns teammates in the current project, based on
+ # project_name.
+ #
+ # @return [Array<Gitlab::Dangerfiles::Teammate>]
+ def project_team(project_name)
+ company_members.select { |member| member.in_project?(project_name) }
+ rescue => err
+ warn("Reviewer roulette failed to load team data: #{err.message}")
+ []
end
end
end