gitlab-dangerfiles 0.1.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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