Skip to content
Snippets Groups Projects
Verified Commit 8f50f15e authored by Nick Thomas's avatar Nick Thomas
Browse files

Reviewer roulette via Danger

parent 2ebfddfa
No related branches found
No related tags found
No related merge requests found
Pipeline #46568766 failed
This commit is part of merge request !24938. Comments created here will be created in the context of that merge request.
......@@ -11,3 +11,4 @@ danger.import_dangerfile(path: 'danger/commit_messages')
danger.import_dangerfile(path: 'danger/duplicate_yarn_dependencies')
danger.import_dangerfile(path: 'danger/prettier')
danger.import_dangerfile(path: 'danger/eslint')
danger.import_dangerfile(path: 'danger/roulette')
# frozen_string_literal: true
require 'net/http'
require 'yaml'
class Teammate
attr_reader :name, :projects, :gitlab
def initialize(options = {})
@name = options['name']
@projects = options['projects']
@gitlab = options['gitlab']
end
def in_project?(name)
projects&.has_key?(name)
end
# Traintainers also count as reviewers
def reviewer?(project, category)
capabilities(project) == "reviewer #{category}" || traintainer?(project, category)
end
def traintainer?(project, category)
capabilities(project) == "trainee_maintainer #{category}"
end
def maintainer?(project, category)
capabilities(project) == "maintainer #{category}"
end
private
def capabilities(project)
projects.fetch(project, '')
end
end
module Danger
# Common helper functions for our danger scripts
# If we find ourselves repeating code in our danger files, we might as well put them in here.
class Helper < Plugin
TEAM_DATA_URL = URI.parse('https://gitlab.com/gitlab-com/www-gitlab-com/raw/master/data/team.yml').freeze
# Returns a list of all files that have been added, modified or renamed.
# `git.modified_files` might contain paths that already have been renamed,
# so we need to remove them from the list.
......@@ -36,6 +74,18 @@ def ee?
ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('../../CHANGELOG-EE.md')
end
def project_name
ee? ? 'gitlab-ee' : 'gitlab-ce'
end
def team
Psych.safe_load(Net::HTTP.get(TEAM_DATA_URL), [Date]).map { |hash| Teammate.new(hash) }
end
def project_team
team.select { |member| member.in_project?(project_name) }
end
# @return [Hash<String,Array<String>>]
def changes_by_category
all_changed_files.inject(Hash.new { |h, k| h[k] = [] }) do |hsh, file|
......@@ -50,7 +100,23 @@ def category_for_file(file)
end
CATEGORIES = {
%r{\Adoc/} => :documentation
}
%r{\Adoc/} => :documentation,
%r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z/} => :documentation,
%r{\A(/ee)?/app/(assets|views)/} => :frontend,
%r{\A(/ee)?/public/} => :frontend,
%r{\A(/ee)?/vendor/assets/} => :frontend,
%r{\A/(jest\.config\.js|package\.json|yarn\.lock)\z} => :frontend,
%r{\A(/ee)?/app/(controllers|finders|graphql|helpers|mailers|models|policies|presenters|serializers|services|uploaders|validators|workers)/} => :backend,
%r{\A(/ee)?/(bin|config|danger|generator_templates|lib|rubocop|scripts|spec)/} => :backend,
%r{\A(/ee)?/vendor/(cert_manager|Dockerfile|gitignore|ingress|jupyter|project_templates|prometheus|runner)/} => :backend,
%r{\A(/ee)?/vendor/(languages\.yml|licenses\.csv)\z/} => :backend,
%r{\A/(Gemfile|Gemfile.lock)\z} => :backend,
%r{\A/(GITALY_SERVER_VERSION|GITLAB_PAGES_VERSION|GITLAB_SHELL_VERSION|GITLAB_WORKHORSE_VERSION|Rakefile)\z} => :backend,
%r{\A(/ee)?/db/} => :database,
%r{\A(/ee)?/qa/} => :qa
}.freeze
end
end
# frozen_string_literal: true
HANDLED_CHANGES = [:backend, :frontend].freeze
MESSAGE = <<MARKDOWN
Changes that require review have been detected! A merge request is normally
reviewed by both a reviewer and a maintainer in its primary category (e.g.
~frontend or ~backend), and by a maintainer in all other categories.
To spread load more evenly across eligible reviewers, Danger has randomly picked
a candidate for each review slot. Feel free to override this selection if you
think someone else would be better-suited, or the chosen person is unavailable.
Once you've decided who will review this merge request, mention them as you
normally would! Danger does not (yet?) automatically notify them for you.
| Category | Reviewer | Maintainer |
| -------- | -------- | ---------- |
MARKDOWN
def markdown_name(teammate)
return nil unless teammate
"`@#{teammate.gitlab}`"
end
def spin_the_wheel(category)
team = helper.project_team
reviewers = team.select { |member| member.reviewer?(helper.project, category) }
maintainers = team.select { |member| member.maintainer?(helper.project, category) }
# TODO: filter out people who are currently not in the office
# TODO: take CODEOWNERS into account?
# TODO: take OOO into account
reviewer = reviewers[rand(reviewers.size)]
maintainer = maintainers[rand(maintainers.size)]
"| #{category.capitalize} | #{markdown_name(reviewer)} | #{markdown_name(maintainer)} |
end
changes = helper.changes_by_category
unless changes.empty?
categories = changes.keys & HANDLED_CHANGES
table = categories.map { |category| spin_the_wheel(team, category) }
markdown([message, *table].join("\n")
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment