gitlab-dangerfiles 0.1.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/lib/danger/helper.rb +155 -113
  3. data/lib/danger/roulette.rb +68 -36
  4. data/lib/gitlab/dangerfiles.rb +0 -34
  5. data/lib/gitlab/dangerfiles/base_linter.rb +96 -0
  6. data/lib/gitlab/dangerfiles/commit_linter.rb +25 -103
  7. data/lib/gitlab/dangerfiles/merge_request_linter.rb +30 -0
  8. data/lib/gitlab/dangerfiles/teammate.rb +23 -9
  9. data/lib/gitlab/dangerfiles/title_linting.rb +38 -0
  10. data/lib/gitlab/dangerfiles/version.rb +1 -1
  11. data/lib/gitlab/dangerfiles/weightage.rb +10 -0
  12. data/lib/gitlab/dangerfiles/weightage/maintainers.rb +33 -0
  13. data/lib/gitlab/dangerfiles/weightage/reviewers.rb +65 -0
  14. metadata +9 -25
  15. data/lib/danger/changelog.rb +0 -39
  16. data/lib/danger/sidekiq_queues.rb +0 -37
  17. data/lib/gitlab/dangerfiles/bundle_size/Dangerfile +0 -38
  18. data/lib/gitlab/dangerfiles/ce_ee_vue_templates/Dangerfile +0 -56
  19. data/lib/gitlab/dangerfiles/changelog/Dangerfile +0 -90
  20. data/lib/gitlab/dangerfiles/changes_size/Dangerfile +0 -17
  21. data/lib/gitlab/dangerfiles/commit_messages/Dangerfile +0 -135
  22. data/lib/gitlab/dangerfiles/database/Dangerfile +0 -67
  23. data/lib/gitlab/dangerfiles/documentation/Dangerfile +0 -29
  24. data/lib/gitlab/dangerfiles/duplicate_yarn_dependencies/Dangerfile +0 -29
  25. data/lib/gitlab/dangerfiles/eslint/Dangerfile +0 -31
  26. data/lib/gitlab/dangerfiles/frozen_string/Dangerfile +0 -28
  27. data/lib/gitlab/dangerfiles/karma/Dangerfile +0 -51
  28. data/lib/gitlab/dangerfiles/metadata/Dangerfile +0 -50
  29. data/lib/gitlab/dangerfiles/popen.rb +0 -55
  30. data/lib/gitlab/dangerfiles/prettier/Dangerfile +0 -41
  31. data/lib/gitlab/dangerfiles/roulette/Dangerfile +0 -97
  32. data/lib/gitlab/dangerfiles/sidekiq_queues/Dangerfile +0 -27
  33. data/lib/gitlab/dangerfiles/specs/Dangerfile +0 -42
  34. data/lib/gitlab/dangerfiles/tasks.rb +0 -19
  35. data/lib/gitlab/dangerfiles/telemetry/Dangerfile +0 -32
  36. data/lib/gitlab/dangerfiles/utility_css/Dangerfile +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6298bb4b8fed9c66cd1bc9b811e9ddb93d6e8474dd8797da837780ec5bcbe1a8
4
- data.tar.gz: 2be36370906090104ebba958d4910d5f105cf3b8d0619849c78dbb5b625dcd9a
3
+ metadata.gz: a41237cf8c1b948162962ffd5712cac36034d6e3f6ddbd551f7b7f5c64315ac6
4
+ data.tar.gz: 9416beb3ed3688db58d83bfafb103a3f21b15c7a1e1dae83fe08dae33b13d329
5
5
  SHA512:
6
- metadata.gz: 9c09bdec93ab9d0b1a73565e62a46e400204907f758cb924c0d27d27eda7aae0871e45a02d7ac1f6846d2aa0f6a8c41d341f57147d5e04d94ac4351a2b6b64d1
7
- data.tar.gz: 67710acb881d9f5229944ccb3dc40bb7d9c4ae0d66743190cfb3fac7e560f22f3ca8a7ca7b7b60119e6f595b4a4e00d3235df0728a79370d60723985cb4d0b93
6
+ metadata.gz: 294c63c0dad1685a71e4110ab5248d96e7efa97ef448dc861c6a443a8123fd490b1fbdf303b87bf7c18802074b24b3b371a433b3ef06c831cc368559c4c832a0
7
+ data.tar.gz: aa3bc70fe5068018df9dd1eee9010732b4df231033434ba02c3872b0902e3850adde1b97b6228dbda9f58e10c032b7d4b6467c96f18392d3e74dfab497638835
data/lib/danger/helper.rb CHANGED
@@ -2,93 +2,72 @@
2
2
 
3
3
  require "net/http"
4
4
  require "json"
5
+ require "danger"
5
6
  require_relative "../gitlab/dangerfiles/teammate"
7
+ require_relative "../gitlab/dangerfiles/title_linting"
6
8
 
7
9
  module Danger
8
10
  # Common helper functions for our danger scripts.
9
11
  class Helper < Danger::Plugin
10
12
  RELEASE_TOOLS_BOT = "gitlab-release-tools-bot"
13
+ DRAFT_REGEX = /\A*#{Regexp.union(/(?i)(\[WIP\]\s*|WIP:\s*|WIP$)/, /(?i)(\[draft\]|\(draft\)|draft:|draft\s\-\s|draft$)/)}+\s*/i.freeze
11
14
  CATEGORY_LABELS = {
12
15
  docs: "~documentation", # Docs are reviewed along DevOps stages, so don't need roulette for now.
13
16
  none: "",
14
17
  qa: "~QA",
15
18
  test: "~test ~Quality for `spec/features/*`",
16
19
  engineering_productivity: '~"Engineering Productivity" for CI, Danger',
17
- }.freeze
18
- # First-match win, so be sure to put more specific regex at the top...
19
- CATEGORIES = {
20
- %r{\Adoc/} => :docs,
21
- %r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z} => :docs,
22
-
23
- %r{\A(ee/)?app/(assets|views)/} => :frontend,
24
- %r{\A(ee/)?public/} => :frontend,
25
- %r{\A(ee/)?spec/(javascripts|frontend)/} => :frontend,
26
- %r{\A(ee/)?vendor/assets/} => :frontend,
27
- %r{\A(ee/)?scripts/frontend/} => :frontend,
28
- %r{(\A|/)(
29
- \.babelrc |
30
- \.eslintignore |
31
- \.eslintrc(\.yml)? |
32
- \.nvmrc |
33
- \.prettierignore |
34
- \.prettierrc |
35
- \.scss-lint.yml |
36
- \.stylelintrc |
37
- \.haml-lint.yml |
38
- \.haml-lint_todo.yml |
39
- babel\.config\.js |
40
- jest\.config\.js |
41
- package\.json |
42
- yarn\.lock |
43
- config/.+\.js
44
- )\z}x => :frontend,
45
-
46
- %r{(\A|/)(
47
- \.gitlab/ci/frontend\.gitlab-ci\.yml
48
- )\z}x => %i[frontend engineering_productivity],
49
-
50
- %r{\A(ee/)?db/(?!fixtures)[^/]+} => :database,
51
- %r{\A(ee/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => :database,
52
- %r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => :database,
53
- %r{\A(ee/)?app/finders/} => :database,
54
- %r{\Arubocop/cop/migration(/|\.rb)} => :database,
55
-
56
- %r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :engineering_productivity,
57
- %r{\A\.codeclimate\.yml\z} => :engineering_productivity,
58
- %r{\A\.overcommit\.yml\.example\z} => :engineering_productivity,
59
- %r{\A\.editorconfig\z} => :engineering_productivity,
60
- %r{Dangerfile\z} => :engineering_productivity,
61
- %r{\A(ee/)?(danger/|lib/gitlab/danger/)} => :engineering_productivity,
62
- %r{\A(ee/)?scripts/} => :engineering_productivity,
63
- %r{\Atooling/} => :engineering_productivity,
64
-
65
- %r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
66
- %r{\A(ee/)?(bin|config|generator_templates|lib|rubocop)/} => :backend,
67
- %r{\A(ee/)?spec/features/} => :test,
68
- %r{\A(ee/)?spec/} => :backend,
69
- %r{\A(ee/)?vendor/} => :backend,
70
- %r{\A(Gemfile|Gemfile.lock|Rakefile)\z} => :backend,
71
- %r{\A[A-Z_]+_VERSION\z} => :backend,
72
- %r{\A\.rubocop(_todo)?\.yml\z} => :backend,
73
- %r{\Afile_hooks/} => :backend,
74
-
75
- %r{\A(ee/)?qa/} => :qa,
76
-
77
- # Files that don't fit into any category are marked with :none
78
- %r{\A(ee/)?changelogs/} => :none,
79
- %r{\Alocale/gitlab\.pot\z} => :none,
80
-
81
- # Fallbacks in case the above patterns miss anything
82
- %r{\.rb\z} => :backend,
83
- %r{(
84
- \.(md|txt)\z |
85
- \.markdownlint\.json
86
- )}x => :none, # To reinstate roulette for documentation, set to `:docs`.
87
- %r{\.js\z} => :frontend,
20
+ ci_template: '~"ci::templates"',
88
21
  }.freeze
89
22
 
90
23
  HTTPError = Class.new(StandardError)
91
24
 
25
+ Change = Struct.new(:file, :change_type, :category)
26
+
27
+ class Changes < ::SimpleDelegator
28
+ def added
29
+ select_by_change_type(:added)
30
+ end
31
+
32
+ def modified
33
+ select_by_change_type(:modified)
34
+ end
35
+
36
+ def deleted
37
+ select_by_change_type(:deleted)
38
+ end
39
+
40
+ def renamed_before
41
+ select_by_change_type(:renamed_before)
42
+ end
43
+
44
+ def renamed_after
45
+ select_by_change_type(:renamed_after)
46
+ end
47
+
48
+ def has_category?(category)
49
+ any? { |change| change.category == category }
50
+ end
51
+
52
+ def by_category(category)
53
+ Changes.new(select { |change| change.category == category })
54
+ end
55
+
56
+ def categories
57
+ map(&:category).uniq
58
+ end
59
+
60
+ def files
61
+ map(&:file)
62
+ end
63
+
64
+ private
65
+
66
+ def select_by_change_type(change_type)
67
+ Changes.new(select { |change| change.change_type == change_type })
68
+ end
69
+ end
70
+
92
71
  def gitlab_helper
93
72
  # Unfortunately the following does not work:
94
73
  # - respond_to?(:gitlab)
@@ -98,10 +77,6 @@ module Danger
98
77
  nil
99
78
  end
100
79
 
101
- def rule_names
102
- ci? ? Gitlab::Dangerfiles::LOCAL_RULES | Gitlab::Dangerfiles::CI_ONLY_RULES : Gitlab::Dangerfiles::LOCAL_RULES
103
- end
104
-
105
80
  def html_link(str)
106
81
  ci? ? gitlab_helper.html_link(str) : str
107
82
  end
@@ -148,23 +123,25 @@ module Danger
148
123
  .sort
149
124
  end
150
125
 
151
- def all_ee_changes
152
- all_changed_files.grep(%r{\Aee/})
153
- end
154
-
155
- def ee?
156
- # Support former project name for `dev` and support local Danger run
157
- %w[gitlab gitlab-ee].include?(ENV["CI_PROJECT_NAME"]) || Dir.exist?("../../ee")
126
+ # Returns a string containing changed lines as git diff
127
+ #
128
+ # Considering changing a line in lib/gitlab/usage_data.rb it will return:
129
+ #
130
+ # [ "--- a/lib/gitlab/usage_data.rb",
131
+ # "+++ b/lib/gitlab/usage_data.rb",
132
+ # "+ # Test change",
133
+ # "- # Old change" ]
134
+ def changed_lines(changed_file)
135
+ diff = git.diff_for_file(changed_file)
136
+ return [] unless diff
137
+
138
+ diff.patch.split("\n").select { |line| %r{^[+-]}.match?(line) }
158
139
  end
159
140
 
160
141
  def release_automation?
161
142
  gitlab_helper&.mr_author == RELEASE_TOOLS_BOT
162
143
  end
163
144
 
164
- def project_name
165
- ee? ? "gitlab" : "gitlab-foss"
166
- end
167
-
168
145
  def markdown_list(items)
169
146
  list = items.map { |item| "* `#{item}`" }.join("\n")
170
147
 
@@ -175,17 +152,51 @@ module Danger
175
152
  end
176
153
  end
177
154
 
178
- # @return [Hash<String,Array<String>>]
179
- def changes_by_category
155
+ # @return [Hash<Symbol,Array<String>>]
156
+ def changes_by_category(categories)
180
157
  all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
181
- categories_for_file(file).each { |category| hash[category] << file }
158
+ categories_for_file(file, categories).each { |category| hash[category] << file }
182
159
  end
183
160
  end
184
161
 
185
- # Determines the categories a file is in, e.g., `[:frontend]`, `[:backend]`, or `%i[frontend engineering_productivity]`.
162
+ # @return [Changes]
163
+ def changes(categories)
164
+ Changes.new([]).tap do |changes|
165
+ git.added_files.each do |file|
166
+ categories_for_file(file, categories).each { |category| changes << Change.new(file, :added, category) }
167
+ end
168
+
169
+ git.modified_files.each do |file|
170
+ categories_for_file(file, categories).each { |category| changes << Change.new(file, :modified, category) }
171
+ end
172
+
173
+ git.deleted_files.each do |file|
174
+ categories_for_file(file, categories).each { |category| changes << Change.new(file, :deleted, category) }
175
+ end
176
+
177
+ git.renamed_files.map { |x| x[:before] }.each do |file|
178
+ categories_for_file(file, categories).each { |category| changes << Change.new(file, :renamed_before, category) }
179
+ end
180
+
181
+ git.renamed_files.map { |x| x[:after] }.each do |file|
182
+ categories_for_file(file, categories).each { |category| changes << Change.new(file, :renamed_after, category) }
183
+ end
184
+ end
185
+ end
186
+
187
+ # Determines the categories a file is in, e.g., `[:frontend]`, `[:backend]`, or `%i[frontend engineering_productivity]`
188
+ # using filename regex and specific change regex if given.
189
+ #
186
190
  # @return Array<Symbol>
187
- def categories_for_file(file)
188
- _, categories = CATEGORIES.find { |regexp, _| regexp.match?(file) }
191
+ def categories_for_file(file, categories)
192
+ _, categories = categories.find do |key, _|
193
+ filename_regex, changes_regex = Array(key)
194
+
195
+ found = filename_regex.match?(file)
196
+ found &&= changed_lines(file).any? { |changed_line| changes_regex.match?(changed_line) } if changes_regex
197
+
198
+ found
199
+ end
189
200
 
190
201
  Array(categories || :unknown)
191
202
  end
@@ -202,43 +213,64 @@ module Danger
202
213
  usernames.map { |u| Gitlab::Dangerfiles::Teammate.new("username" => u) }
203
214
  end
204
215
 
205
- def missing_database_labels(current_mr_labels)
206
- labels = if has_database_scoped_labels?(current_mr_labels)
207
- ["database"]
208
- else
209
- ["database", "database::review pending"]
210
- end
216
+ def mr_iid
217
+ return "" unless gitlab_helper
211
218
 
212
- labels - current_mr_labels
219
+ gitlab_helper.mr_json["iid"]
213
220
  end
214
221
 
215
- def sanitize_mr_title(title)
216
- title.gsub(/^WIP: */, "").gsub(/`/, '\\\`')
222
+ def mr_title
223
+ return "" unless gitlab_helper
224
+
225
+ gitlab_helper.mr_json["title"]
217
226
  end
218
227
 
219
- def security_mr?
220
- return false unless gitlab_helper
228
+ def mr_web_url
229
+ return "" unless gitlab_helper
230
+
231
+ gitlab_helper.mr_json["web_url"]
232
+ end
233
+
234
+ def mr_labels
235
+ return [] unless gitlab_helper
236
+
237
+ gitlab_helper.mr_labels
238
+ end
239
+
240
+ def mr_target_branch
241
+ return "" unless gitlab_helper
221
242
 
222
- gitlab_helper.mr_json["web_url"].include?("/gitlab-org/security/")
243
+ gitlab_helper.mr_json["target_branch"]
244
+ end
245
+
246
+ def draft_mr?
247
+ Gitlab::Dangerfiles::TitleLinting.has_draft_flag?(mr_title)
248
+ end
249
+
250
+ def security_mr?
251
+ mr_web_url.include?("/gitlab-org/security/")
223
252
  end
224
253
 
225
254
  def cherry_pick_mr?
226
- return false unless gitlab_helper
255
+ Gitlab::Dangerfiles::TitleLinting.has_cherry_pick_flag?(mr_title)
256
+ end
227
257
 
228
- /cherry[\s-]*pick/i.match?(gitlab_helper.mr_json["title"])
258
+ def run_all_rspec_mr?
259
+ Gitlab::Dangerfiles::TitleLinting.has_run_all_rspec_flag?(mr_title)
229
260
  end
230
261
 
231
- def stable_branch?
232
- return false unless gitlab_helper
262
+ def run_as_if_foss_mr?
263
+ Gitlab::Dangerfiles::TitleLinting.has_run_as_if_foss_flag?(mr_title)
264
+ end
233
265
 
234
- /\A\d+-\d+-stable-ee/i.match?(gitlab_helper.mr_json["target_branch"])
266
+ def stable_branch?
267
+ /\A\d+-\d+-stable-ee/i.match?(mr_target_branch)
235
268
  end
236
269
 
237
270
  def mr_has_labels?(*labels)
238
- return false unless gitlab_helper
239
-
240
271
  labels = labels.flatten.uniq
241
- (labels & gitlab_helper.mr_labels) == labels
272
+
273
+ (labels & mr_labels) == labels
242
274
  end
243
275
 
244
276
  def labels_list(labels, sep: ", ")
@@ -251,10 +283,20 @@ module Danger
251
283
  "/label #{labels_list(labels, sep: " ")}"
252
284
  end
253
285
 
254
- private
286
+ def changed_files(regex)
287
+ all_changed_files.grep(regex)
288
+ end
255
289
 
256
290
  def has_database_scoped_labels?(current_mr_labels)
257
291
  current_mr_labels.any? { |label| label.start_with?("database::") }
258
292
  end
293
+
294
+ def has_ci_changes?
295
+ changed_files(%r{\A(\.gitlab-ci\.yml|\.gitlab/ci/)}).any?
296
+ end
297
+
298
+ def group_label(labels)
299
+ labels.find { |label| label.start_with?("group::") }
300
+ end
259
301
  end
260
302
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../gitlab/dangerfiles/teammate"
4
+ require_relative "../gitlab/dangerfiles/weightage/maintainers"
5
+ require_relative "../gitlab/dangerfiles/weightage/reviewers"
4
6
 
5
7
  module Danger
6
8
  # Common helper functions for our danger scripts. See Danger::Helper
@@ -9,42 +11,58 @@ module Danger
9
11
  ROULETTE_DATA_URL = "https://gitlab-org.gitlab.io/gitlab-roulette/roulette.json"
10
12
  HOURS_WHEN_PERSON_CAN_BE_PICKED = (6..14).freeze
11
13
 
12
- Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role)
14
+ INCLUDE_TIMEZONE_FOR_CATEGORY = {
15
+ database: false,
16
+ }.freeze
17
+
18
+ Spin = Struct.new(:category, :reviewer, :maintainer, :optional_role, :timezone_experiment)
19
+
20
+ def team_mr_author
21
+ team.find { |person| person.username == mr_author_username }
22
+ end
13
23
 
14
24
  # Assigns GitLab team members to be reviewer and maintainer
15
25
  # for each change category that a Merge Request contains.
16
26
  #
17
27
  # @return [Array<Spin>]
18
- def spin(project, categories, branch_name, timezone_experiment: false)
19
- team = begin
20
- project_team(project)
21
- rescue => err
22
- warn("Reviewer roulette failed to load team data: #{err.message}")
23
- []
24
- end
28
+ def spin(project, categories, timezone_experiment: false)
29
+ spins = categories.sort.map do |category|
30
+ including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(category, timezone_experiment)
25
31
 
26
- canonical_branch_name = canonical_branch_name(branch_name)
27
-
28
- spin_per_category = categories.each_with_object({}) do |category, memo|
29
- memo[category] = spin_for_category(team, project, category, canonical_branch_name, timezone_experiment: timezone_experiment)
32
+ spin_for_category(project, category, timezone_experiment: including_timezone)
30
33
  end
31
34
 
32
- spin_per_category.map do |category, spin|
33
- case category
35
+ backend_spin = spins.find { |spin| spin.category == :backend }
36
+
37
+ spins.each do |spin|
38
+ including_timezone = INCLUDE_TIMEZONE_FOR_CATEGORY.fetch(spin.category, timezone_experiment)
39
+ case spin.category
40
+ when :qa
41
+ # MR includes QA changes, but also other changes, and author isn't an SET
42
+ if categories.size > 1 && !team_mr_author&.any_capability?(project, spin.category)
43
+ spin.optional_role = :maintainer
44
+ end
34
45
  when :test
46
+ spin.optional_role = :maintainer
47
+
35
48
  if spin.reviewer.nil?
36
49
  # Fetch an already picked backend reviewer, or pick one otherwise
37
- spin.reviewer = spin_per_category[:backend]&.reviewer || spin_for_category(team, project, :backend, canonical_branch_name).reviewer
50
+ spin.reviewer = backend_spin&.reviewer || spin_for_category(project, :backend, timezone_experiment: including_timezone).reviewer
38
51
  end
39
52
  when :engineering_productivity
40
53
  if spin.maintainer.nil?
41
54
  # Fetch an already picked backend maintainer, or pick one otherwise
42
- spin.maintainer = spin_per_category[:backend]&.maintainer || spin_for_category(team, project, :backend, canonical_branch_name).maintainer
55
+ spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
56
+ end
57
+ when :ci_template
58
+ if spin.maintainer.nil?
59
+ # Fetch an already picked backend maintainer, or pick one otherwise
60
+ spin.maintainer = backend_spin&.maintainer || spin_for_category(project, :backend, timezone_experiment: including_timezone).maintainer
43
61
  end
44
62
  end
45
-
46
- spin
47
63
  end
64
+
65
+ spins
48
66
  end
49
67
 
50
68
  # Looks up the current list of GitLab team members and parses it into a
@@ -66,14 +84,9 @@ module Danger
66
84
  # @return [Array<Teammate>]
67
85
  def project_team(project_name)
68
86
  team.select { |member| member.in_project?(project_name) }
69
- end
70
-
71
- def canonical_branch_name(branch_name)
72
- branch_name.gsub(/^[ce]e-|-[ce]e$/, "")
73
- end
74
-
75
- def new_random(seed)
76
- Random.new(Digest::MD5.hexdigest(seed).to_i(16))
87
+ rescue => err
88
+ warn("Reviewer roulette failed to load team data: #{err.message}")
89
+ []
77
90
  end
78
91
 
79
92
  # Known issue: If someone is rejected due to OOO, and then becomes not OOO, the
@@ -106,30 +119,49 @@ module Danger
106
119
  # @param [Teammate] person
107
120
  # @return [Boolean]
108
121
  def mr_author?(person)
109
- person.username == gitlab.mr_author
122
+ person.username == mr_author_username
123
+ end
124
+
125
+ def mr_author_username
126
+ helper.gitlab_helper&.mr_author || `whoami`
127
+ end
128
+
129
+ def mr_source_branch
130
+ return `git rev-parse --abbrev-ref HEAD` unless helper.gitlab_helper&.mr_json
131
+
132
+ helper.gitlab_helper.mr_json["source_branch"]
133
+ end
134
+
135
+ def mr_labels
136
+ helper.gitlab_helper&.mr_labels || []
137
+ end
138
+
139
+ def new_random(seed)
140
+ Random.new(Digest::MD5.hexdigest(seed).to_i(16))
110
141
  end
111
142
 
112
143
  def spin_role_for_category(team, role, project, category)
113
144
  team.select do |member|
114
- member.public_send("#{role}?", project, category, gitlab.mr_labels) # rubocop:disable GitlabSecurity/PublicSend
145
+ member.public_send("#{role}?", project, category, mr_labels) # rubocop:disable GitlabSecurity/PublicSend
115
146
  end
116
147
  end
117
148
 
118
- def spin_for_category(team, project, category, branch_name, timezone_experiment: false)
149
+ def spin_for_category(project, category, timezone_experiment: false)
150
+ team = project_team(project)
119
151
  reviewers, traintainers, maintainers =
120
152
  %i[reviewer traintainer maintainer].map do |role|
121
153
  spin_role_for_category(team, role, project, category)
122
154
  end
123
155
 
124
- # TODO: take CODEOWNERS into account?
125
- # https://gitlab.com/gitlab-org/gitlab/issues/26723
156
+ random = new_random(mr_source_branch)
157
+
158
+ weighted_reviewers = Gitlab::Dangerfiles::Weightage::Reviewers.new(reviewers, traintainers).execute
159
+ weighted_maintainers = Gitlab::Dangerfiles::Weightage::Maintainers.new(maintainers).execute
126
160
 
127
- # Make traintainers have triple the chance to be picked as a reviewer
128
- random = new_random(branch_name)
129
- reviewer = spin_for_person(reviewers + traintainers + traintainers, random: random, timezone_experiment: timezone_experiment)
130
- maintainer = spin_for_person(maintainers, random: random, timezone_experiment: timezone_experiment)
161
+ reviewer = spin_for_person(weighted_reviewers, random: random, timezone_experiment: timezone_experiment)
162
+ maintainer = spin_for_person(weighted_maintainers, random: random, timezone_experiment: timezone_experiment)
131
163
 
132
- Spin.new(category, reviewer, maintainer)
164
+ Spin.new(category, reviewer, maintainer, false, timezone_experiment)
133
165
  end
134
166
  end
135
167
  end