gitlab-dangerfiles 4.8.1 → 4.12.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.
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../gitlab/dangerfiles/commit_linter"
4
+ require_relative "../../gitlab/dangerfiles/merge_request_linter"
5
+
6
+ module Danger
7
+ # Common helper functions for our danger scripts. See Danger::Helper
8
+ # for more details
9
+ class CommitMessages < Danger::Plugin
10
+ COMMIT_MSG_DOC = "https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines"
11
+ MORE_INFO = "For more information, take a look at our [Commit message guidelines](#{COMMIT_MSG_DOC}).".freeze
12
+ THE_DANGER_JOB_TEXT = "the `%<job_name>s` job"
13
+ MAX_COMMITS_COUNT_EXCEEDED_MESSAGE = <<~MSG
14
+ This merge request includes more than %<max_commits_count>d commits. Each commit should meet the following criteria:
15
+
16
+ 1. Have a well-written commit message.
17
+ 1. Has all tests passing when used on its own (e.g. when using git checkout SHA).
18
+ 1. Can be reverted on its own without also requiring the revert of commit that came before it.
19
+ 1. Is small enough that it can be reviewed in isolation in under 30 minutes or so.
20
+
21
+ If this merge request contains commits that do not meet this criteria and/or contains intermediate work, please rebase these commits into a smaller number of commits or split this merge request into multiple smaller merge requests.
22
+ MSG
23
+
24
+ def build_message(commit, message, more_info: true)
25
+ [message].tap do |full_message|
26
+ full_message << ". #{MORE_INFO}" if more_info
27
+ full_message.unshift("#{commit.sha}: ") if commit.sha
28
+ end.join
29
+ end
30
+
31
+ def fail_commit(commit, message, more_info: true)
32
+ self.fail(build_message(commit, message, more_info: more_info))
33
+ end
34
+
35
+ def warn_commit(commit, message, more_info: true)
36
+ self.warn(build_message(commit, message, more_info: more_info))
37
+ end
38
+
39
+ def danger_job_link
40
+ if helper.ci?
41
+ "[#{format(THE_DANGER_JOB_TEXT, job_name: ENV.fetch('CI_JOB_NAME', nil))}](#{ENV.fetch('CI_JOB_URL', nil)})"
42
+ else
43
+ THE_DANGER_JOB_TEXT
44
+ end
45
+ end
46
+
47
+ # Perform various checks against commits. We're not using
48
+ # https://github.com/jonallured/danger-commit_lint because its output is not
49
+ # very helpful, and it doesn't offer the means of ignoring merge commits.
50
+ def lint_commit(commit)
51
+ linter = Gitlab::Dangerfiles::CommitLinter.new(commit)
52
+
53
+ # For now we'll ignore merge commits, as getting rid of those is a problem
54
+ # separate from enforcing good commit messages.
55
+ return linter if linter.merge?
56
+
57
+ # We ignore revert commits as they are well structured by Git already
58
+ return linter if linter.revert?
59
+
60
+ # If MR is set to squash, we ignore fixup commits
61
+ return linter if linter.fixup? && helper.squash_mr?
62
+
63
+ if linter.fixup?
64
+ msg = "Squash or fixup commits must be squashed before merge, or **edit** the merge request, " \
65
+ "enable **Squash commits when merge request is accepted** and re-run #{danger_job_link}."
66
+
67
+ if helper.draft_mr? || helper.squash_mr?
68
+ warn_commit(commit, msg, more_info: false)
69
+ else
70
+ fail_commit(commit, msg, more_info: false)
71
+ end
72
+
73
+ # Makes no sense to process other rules for fixup commits, they trigger just more noise
74
+ return linter
75
+ end
76
+
77
+ # Fail if a suggestion commit is used and squash is not enabled
78
+ if linter.suggestion?
79
+ unless helper.squash_mr?
80
+ msg = "If you are applying suggestions, **edit** the merge request, enable **Squash commits when " \
81
+ "merge request is accepted** and re-run #{danger_job_link}."
82
+ fail_commit(commit, msg, more_info: false)
83
+ end
84
+
85
+ return linter
86
+ end
87
+
88
+ linter.lint
89
+ end
90
+
91
+ def lint_mr_title(mr_title)
92
+ commit = Struct.new(:message, :sha).new(mr_title)
93
+
94
+ Gitlab::Dangerfiles::MergeRequestLinter.new(commit).lint
95
+ end
96
+
97
+ def count_non_fixup_commits(commit_linters)
98
+ commit_linters.count { |commit_linter| !commit_linter.fixup? }
99
+ end
100
+
101
+ def lint_commits(commits)
102
+ commit_linters = commits.map { |commit| lint_commit(commit) }
103
+
104
+ if helper.squash_mr?
105
+ multi_line_commit_linter = commit_linters.detect do |commit_linter|
106
+ !commit_linter.merge? && commit_linter.multi_line?
107
+ end
108
+
109
+ if multi_line_commit_linter&.failed?
110
+ warn_or_fail_commits(multi_line_commit_linter)
111
+ commit_linters.delete(multi_line_commit_linter) # Don't show an error (here) and a warning (below)
112
+ end
113
+ elsif count_non_fixup_commits(commit_linters) > max_commits_count
114
+ self.warn(format(MAX_COMMITS_COUNT_EXCEEDED_MESSAGE, max_commits_count: max_commits_count))
115
+ end
116
+
117
+ failed_commit_linters = commit_linters.select(&:failed?)
118
+ warn_or_fail_commits(failed_commit_linters, default_to_fail: !helper.squash_mr?)
119
+ end
120
+
121
+ def max_commits_count
122
+ helper.config.max_commits_count
123
+ end
124
+
125
+ def warn_or_fail_commits(failed_linters, default_to_fail: true)
126
+ level = default_to_fail ? :fail : :warn
127
+
128
+ Array(failed_linters).each do |linter|
129
+ linter.problems.each do |problem_key, problem_desc|
130
+ case problem_key
131
+ when :subject_too_short, :details_too_many_changes, :details_line_too_long
132
+ warn_commit(linter.commit, problem_desc)
133
+ else
134
+ __send__(:"#{level}_commit", linter.commit, problem_desc)
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Danger
4
+ # Contains method to check if Duo code suggestion added to MR as a reviewer.
5
+ class DuoCode < Danger::Plugin
6
+ def suggestion_added?
7
+ return false unless helper.ci?
8
+
9
+ helper.mr_reviewers.include?("GitLabDuo")
10
+ end
11
+ end
12
+ end
@@ -25,12 +25,14 @@ module Danger
25
25
  pipeline: '~"maintenance::pipelines" for CI',
26
26
  ci_template: '~"ci::templates"',
27
27
  analytics_instrumentation: '~"analytics instrumentation"',
28
- import_integrate_be: '~"group::import and integrate" (backend)',
29
- import_integrate_fe: '~"group::import and integrate" (frontend)',
30
28
  Authentication: '~"group::authentication"',
31
29
  Authorization: '~"group::authorization"',
32
30
  Compliance: '~"group::compliance"',
33
- Verify: '~"devops::verify"'
31
+ Verify: '~"devops::verify"',
32
+ "AST:Composition Analysis": '~"group::composition analysis"',
33
+ "AST:Dynamic Analysis": '~"group::dynamic analysis"',
34
+ "AST:Secret Detection": '~"group::secret detection"',
35
+ "AST:Static Analysis": '~"group::static analysis"'
34
36
  }.freeze
35
37
  # rubocop:enable Style/HashSyntax
36
38
 
@@ -181,7 +183,9 @@ module Danger
181
183
  # @return [{Symbol => Array<String>}] a hash of the type +{ category1: ["file1", "file2"], category2: ["file3", "file4"] }+
182
184
  # using filename regex (+filename_regex+) and specific change regex (+changes_regex+) from the given +categories+ hash.
183
185
  def changes_by_category(categories = [])
184
- all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
186
+ changed_and_deleted_files = all_changed_files + changes.deleted.files
187
+
188
+ changed_and_deleted_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash|
185
189
  categories_for_file(file, categories).each { |category| hash[category] << file }
186
190
  end
187
191
  end
@@ -244,7 +248,7 @@ module Danger
244
248
  # @return [String] the GFM for a category label, making its best guess if it's not
245
249
  # a category we know about.
246
250
  def label_for_category(category)
247
- CATEGORY_LABELS[category] ||
251
+ helper.config.custom_labels_for_categories[category] || CATEGORY_LABELS[category] ||
248
252
 
249
253
  if category.start_with?("`")
250
254
  category.to_s
@@ -288,6 +292,13 @@ module Danger
288
292
  gitlab_helper.mr_json["assignees"]
289
293
  end
290
294
 
295
+ # @return [Array<Hash>] +[]+ when not in the CI context, and the MR reviewers otherwise.
296
+ def mr_reviewers
297
+ return [] unless ci?
298
+
299
+ gitlab_helper.mr_json["reviewers"]
300
+ end
301
+
291
302
  # @return [String] +""+ when not in the CI context, and the MR title otherwise.
292
303
  def mr_title
293
304
  return "" unless ci?
@@ -9,6 +9,7 @@ module Danger
9
9
  # for more details
10
10
  class Roulette < Danger::Plugin
11
11
  HOURS_WHEN_PERSON_CAN_BE_PICKED = (6..14).freeze
12
+ BOT_REVIEWERS = %w[GitLabDuo].freeze
12
13
  HTTPError = Class.new(StandardError)
13
14
 
14
15
  def prepare_categories(changes_keys)
@@ -45,7 +46,7 @@ module Danger
45
46
  # @param categories [Array<Symbol>] An array of categories symbols.
46
47
  #
47
48
  # @return [Array<Spin>]
48
- def spin(project = nil, categories = [:none], ux_fallback_wider_community_reviewer: teammate_pedroms)
49
+ def spin(project = nil, categories = [:none], ux_fallback_wider_community_reviewer: ux_fallback_reviewer)
49
50
  # TODO: Deprecate the project argument. It prevents us from
50
51
  # memorizing Spinner and can cause unexpected results if it's
51
52
  # passing a different project than the merge request project.
@@ -87,10 +88,40 @@ module Danger
87
88
  Gitlab::Dangerfiles::Teammate.warnings
88
89
  end
89
90
 
91
+ # Automatically assigns reviewers from roulette spins if configured to do so
92
+ #
93
+ # @param spins [Array<Spin>] The roulette spins to potentially assign from
94
+ def assign_reviewers_from_roulette(spins)
95
+ return if human_reviewers?
96
+
97
+ reviewers_to_assign = find_reviewers_to_assign(spins)
98
+
99
+ if reviewers_to_assign.any?
100
+ post_assignment_message(reviewers_to_assign)
101
+ else
102
+ warn("No reviewers available for assignment")
103
+ end
104
+ end
105
+
106
+ # Determines if auto-assignment should happen based on configuration
107
+ #
108
+ # @return [Boolean]
109
+ def auto_assign_reviewers?
110
+ return false if helper.config.auto_assign_for_roulette_roles.empty?
111
+
112
+ configured_labels = helper.config.auto_assign_for_roulette_labels
113
+
114
+ return true if configured_labels.empty?
115
+
116
+ mr_labels = helper.mr_labels
117
+ configured_labels.any? { |label| mr_labels.include?(label) }
118
+ end
119
+
90
120
  private
91
121
 
92
- def teammate_pedroms
93
- @teammate_pedroms ||= Gitlab::Dangerfiles::Teammate.find_member("pedroms")
122
+ def ux_fallback_reviewer
123
+ teammates = %w[pedroms annabeldunstone seggenberger jmiocene clavimoniere]
124
+ Gitlab::Dangerfiles::Teammate.find_member(teammates.sample)
94
125
  end
95
126
 
96
127
  def spin_for_approval_rule?(rule)
@@ -157,5 +188,48 @@ module Danger
157
188
  def labels
158
189
  @labels ||= helper.mr_labels
159
190
  end
191
+
192
+ # Checks if any non-bot reviewers are already assigned to the merge request
193
+ #
194
+ # @return [Boolean]
195
+ def human_reviewers?
196
+ helper.mr_reviewers.any? { |reviewer| !BOT_REVIEWERS.include?(reviewer["username"]) }
197
+ end
198
+
199
+ # Find reviewers to assign based on configured roles
200
+ #
201
+ # @param spins [Array<Spin>] The roulette spins
202
+ # @return [Array<String>] Array of usernames to assign
203
+ def find_reviewers_to_assign(spins)
204
+ roles_to_assign = helper.config.auto_assign_for_roulette_roles
205
+ reviewers_to_assign = []
206
+
207
+ spins.each do |spin|
208
+ if roles_to_assign.include?(:reviewer) && spin.reviewer&.username
209
+ reviewers_to_assign << spin.reviewer.username
210
+ end
211
+
212
+ if roles_to_assign.include?(:maintainer) && spin.maintainer&.username
213
+ reviewers_to_assign << spin.maintainer.username
214
+ end
215
+
216
+ if reviewers_to_assign.any?
217
+ break
218
+ end
219
+ end
220
+
221
+ reviewers_to_assign
222
+ end
223
+
224
+ # Posts the assignment message with the selected reviewers
225
+ #
226
+ # @param reviewers_to_assign [Array<String>] Array of usernames to assign
227
+ def post_assignment_message(reviewers_to_assign)
228
+ role_text = helper.config.auto_assign_for_roulette_roles.map(&:to_s).join(' and ')
229
+ message = "🎲 Assigned #{role_text}s based on reviewer roulette.\n/assign_reviewer #{reviewers_to_assign.map { |u| "@#{u}" }.join(' ')}"
230
+ markdown(message)
231
+ rescue StandardError => e
232
+ warn("Failed to assign reviewers: #{e.message}")
233
+ end
160
234
  end
161
235
  end
@@ -1,127 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../../gitlab/dangerfiles/commit_linter"
4
- require_relative "../../../gitlab/dangerfiles/merge_request_linter"
5
-
6
- COMMIT_MESSAGE_GUIDELINES = "https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines"
7
- MORE_INFO = "For more information, take a look at our [Commit message guidelines](#{COMMIT_MESSAGE_GUIDELINES})."
8
- THE_DANGER_JOB_TEXT = "the `%<job_name>s` job"
9
- MAX_COMMITS_COUNT = helper.config.max_commits_count
10
- MAX_COMMITS_COUNT_EXCEEDED_MESSAGE = <<~MSG
11
- This merge request includes more than %<max_commits_count>d commits. Each commit should meet the following criteria:
12
-
13
- 1. Have a well-written commit message.
14
- 1. Has all tests passing when used on its own (e.g. when using git checkout SHA).
15
- 1. Can be reverted on its own without also requiring the revert of commit that came before it.
16
- 1. Is small enough that it can be reviewed in isolation in under 30 minutes or so.
17
-
18
- If this merge request contains commits that do not meet this criteria and/or contains intermediate work, please rebase these commits into a smaller number of commits or split this merge request into multiple smaller merge requests.
19
- MSG
20
-
21
- def fail_commit(commit, message, more_info: true)
22
- self.fail(build_message(commit, message, more_info: more_info))
23
- end
24
-
25
- def warn_commit(commit, message, more_info: true)
26
- self.warn(build_message(commit, message, more_info: more_info))
27
- end
28
-
29
- def build_message(commit, message, more_info: true)
30
- [message].tap do |full_message|
31
- full_message << ". #{MORE_INFO}" if more_info
32
- full_message.unshift("#{commit.sha}: ") if commit.sha
33
- end.join
34
- end
35
-
36
- def danger_job_link
37
- helper.ci? ? "[#{format(THE_DANGER_JOB_TEXT, job_name: ENV["CI_JOB_NAME"])}](#{ENV['CI_JOB_URL']})" : THE_DANGER_JOB_TEXT
38
- end
39
-
40
- # Perform various checks against commits. We're not using
41
- # https://github.com/jonallured/danger-commit_lint because its output is not
42
- # very helpful, and it doesn't offer the means of ignoring merge commits.
43
- def lint_commit(commit)
44
- linter = Gitlab::Dangerfiles::CommitLinter.new(commit)
45
-
46
- # For now we'll ignore merge commits, as getting rid of those is a problem
47
- # separate from enforcing good commit messages.
48
- return linter if linter.merge?
49
-
50
- # We ignore revert commits as they are well structured by Git already
51
- return linter if linter.revert?
52
-
53
- # If MR is set to squash, we ignore fixup commits
54
- return linter if linter.fixup? && helper.squash_mr?
55
-
56
- if linter.fixup?
57
- msg = "Squash or fixup commits must be squashed before merge, or **edit** the merge request, enable **Squash commits when merge request is accepted** and re-run #{danger_job_link}."
58
- if helper.draft_mr? || helper.squash_mr?
59
- warn_commit(commit, msg, more_info: false)
60
- else
61
- fail_commit(commit, msg, more_info: false)
62
- end
63
-
64
- # Makes no sense to process other rules for fixup commits, they trigger just more noise
65
- return linter
66
- end
67
-
68
- # Fail if a suggestion commit is used and squash is not enabled
69
- if linter.suggestion?
70
- unless helper.squash_mr?
71
- fail_commit(commit, "If you are applying suggestions, **edit** the merge request, enable **Squash commits when merge request is accepted** and re-run #{danger_job_link}.", more_info: false)
72
- end
73
-
74
- return linter
75
- end
76
-
77
- linter.lint
78
- end
79
-
80
- def lint_mr_title(mr_title)
81
- commit = Struct.new(:message, :sha).new(mr_title)
82
-
83
- Gitlab::Dangerfiles::MergeRequestLinter.new(commit).lint
84
- end
85
-
86
- def count_non_fixup_commits(commit_linters)
87
- commit_linters.count { |commit_linter| !commit_linter.fixup? }
88
- end
89
-
90
- def lint_commits(commits)
91
- commit_linters = commits.map { |commit| lint_commit(commit) }
92
-
93
- if helper.squash_mr?
94
- multi_line_commit_linter = commit_linters.detect { |commit_linter| !commit_linter.merge? && commit_linter.multi_line? }
95
-
96
- if multi_line_commit_linter && multi_line_commit_linter.failed?
97
- warn_or_fail_commits(multi_line_commit_linter)
98
- commit_linters.delete(multi_line_commit_linter) # Don't show an error (here) and a warning (below)
99
- end
100
- else
101
- if count_non_fixup_commits(commit_linters) > MAX_COMMITS_COUNT
102
- self.warn(format(MAX_COMMITS_COUNT_EXCEEDED_MESSAGE, max_commits_count: MAX_COMMITS_COUNT))
103
- end
104
- end
105
-
106
- failed_commit_linters = commit_linters.select { |commit_linter| commit_linter.failed? }
107
- warn_or_fail_commits(failed_commit_linters, default_to_fail: !helper.squash_mr?)
108
- end
109
-
110
- def warn_or_fail_commits(failed_linters, default_to_fail: true)
111
- level = default_to_fail ? :fail : :warn
112
-
113
- Array(failed_linters).each do |linter|
114
- linter.problems.each do |problem_key, problem_desc|
115
- case problem_key
116
- when :subject_too_short, :subject_above_warning, :details_too_many_changes, :details_line_too_long
117
- warn_commit(linter.commit, problem_desc)
118
- else
119
- self.__send__(:"#{level}_commit", linter.commit, problem_desc)
120
- end
121
- end
122
- end
123
- end
124
-
125
3
  # As part of https://gitlab.com/groups/gitlab-org/-/epics/4826 we are
126
4
  # vendoring workhorse commits from the stand-alone gitlab-workhorse
127
5
  # repo. There is no point in linting commits that we want to vendor as
@@ -130,4 +8,4 @@ def workhorse_changes?
130
8
  git.diff.any? { |file| file.path.start_with?('workhorse/') }
131
9
  end
132
10
 
133
- lint_commits(git.commits) unless workhorse_changes?
11
+ commit_messages.lint_commits(git.commits) unless workhorse_changes?
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ return if duo_code.suggestion_added?
4
+
5
+ case helper.config.duo_code_review
6
+ when :optional
7
+ markdown("We advise getting a review from GitLab Duo Code. You can assign `@GitLabDuo` as a reviewer to this merge request.")
8
+ when :mandatory
9
+ failure("A review from GitLab Duo Code is mandatory. Please assign `@GitLabDuo` as a reviewer to this merge request.")
10
+ end
@@ -8,9 +8,6 @@ MESSAGE = <<MARKDOWN
8
8
  Changes that require review have been detected! A merge request is normally
9
9
  reviewed by both a reviewer and a maintainer in its primary category and by a
10
10
  maintainer in all other categories.
11
- MARKDOWN
12
-
13
- TABLE_MARKDOWN = <<MARKDOWN
14
11
 
15
12
  To spread load more evenly across eligible reviewers, Danger has picked a candidate for each
16
13
  review slot. Feel free to
@@ -26,9 +23,11 @@ Please consider assigning a reviewer or maintainer who is a
26
23
  [domain expert](https://about.gitlab.com/handbook/engineering/projects/##{PROJECT_NAME})
27
24
  in the area of the merge request.
28
25
 
26
+ MARKDOWN
27
+
28
+ NO_AUTO_ASSIGN = <<MARKDOWN
29
29
  Once you've decided who will review this merge request, mention them as you
30
30
  normally would! Danger does not automatically notify them for you.
31
-
32
31
  MARKDOWN
33
32
 
34
33
  TABLE_HEADER_WITH_CATEGORIES = <<MARKDOWN
@@ -101,5 +100,11 @@ if changes.any?
101
100
 
102
101
  markdown(format(WARNING_MESSAGE, warnings: warnings)) if warnings
103
102
 
104
- markdown(TABLE_MARKDOWN + table_header + rows.join("\n")) unless rows.empty?
103
+ markdown(table_header + rows.join("\n")) unless rows.empty?
104
+
105
+ if roulette.auto_assign_reviewers?
106
+ roulette.assign_reviewers_from_roulette(random_roulette_spins)
107
+ else
108
+ markdown(NO_AUTO_ASSIGN)
109
+ end
105
110
  end
@@ -4,18 +4,18 @@ require_relative "spin"
4
4
 
5
5
  module Gitlab
6
6
  module Dangerfiles
7
- Approval = Struct.new(:category, :spin) do
7
+ Approval = Struct.new(:category, :spin, :name) do
8
8
  def self.from_approval_rule(rule, maintainer)
9
9
  category =
10
10
  if rule["section"] == "codeowners"
11
11
  "`#{rule['name']}`"
12
12
  else
13
- rule["section"]
13
+ rule["section"]&.downcase
14
14
  end.to_sym
15
15
 
16
16
  spin = Spin.new(category, nil, maintainer, :reviewer)
17
17
 
18
- new(category, spin)
18
+ new(category, spin, rule["name"])
19
19
  end
20
20
  end
21
21
  end
@@ -15,8 +15,6 @@ module Gitlab
15
15
  none: None,
16
16
  test: Test,
17
17
  tooling: Tooling,
18
- import_integrate_be: ImportIntegrateBE,
19
- import_integrate_fe: ImportIntegrateFE,
20
18
  ux: UX
21
19
  }.freeze
22
20
  end
@@ -60,20 +58,6 @@ module Gitlab
60
58
  end
61
59
  end
62
60
 
63
- class ImportIntegrateBE < Capability
64
- def has_capability?(teammate)
65
- kind == :reviewer &&
66
- teammate.role.match?(/Backend Engineer.+Manage:Import and Integrate/)
67
- end
68
- end
69
-
70
- class ImportIntegrateFE < Capability
71
- def has_capability?(teammate)
72
- kind == :reviewer &&
73
- teammate.role.match?(/Frontend Engineer.+Manage:Import and Integrate/)
74
- end
75
- end
76
-
77
61
  class UX < Capability
78
62
  def has_capability?(teammate)
79
63
  super && teammate.member_of_the_group?(labels)
@@ -9,9 +9,9 @@ module Gitlab
9
9
  MAX_CHANGED_FILES_IN_COMMIT = 3
10
10
  MAX_CHANGED_LINES_IN_COMMIT = 30
11
11
  # Issue, MR, Epic
12
- SHORT_REFERENCE_REGEX = %r{(\S*([\w\-\/]+)?(?<!`)(#|!|&)\d+(?<!`))}.freeze
12
+ SHORT_REFERENCE_REGEX = %r{(\S*([\w\-\/]+)?(?<!`)(#|!|&)(?>\d+)(?!`))}.freeze
13
13
  # Milestone
14
- MS_SHORT_REFERENCE_REGEX = %r{(\S*([\w\-\/]+)?(?<!`)%"?\d{1,3}\.\d{1,3}"?(?<!`))}.freeze
14
+ MS_SHORT_REFERENCE_REGEX = %r{(\S*([\w\-\/]+)?(?<!`)%(?>"?\d{1,3}\.\d{1,3}"?)(?!`))}.freeze
15
15
  SUGGESTIONS_APPLIED_COMMIT_REGEX = /Apply \d+ suggestion\(s\) to \d+ file\(s\)/.freeze
16
16
 
17
17
  def self.problems_mapping
@@ -25,8 +25,8 @@ module Gitlab
25
25
  "to the commit message, and are displayed as plain text outside of GitLab",
26
26
  message_contains_unicode_emoji: "Avoid the use of Unicode Emoji. These add no value to the commit " \
27
27
  "message, and may not be displayed properly everywhere",
28
- message_contains_short_reference: "Use full URLs instead of short references (`gitlab-org/gitlab#123` or " \
29
- "`!123`), as short references are displayed as plain text outside of GitLab"
28
+ message_contains_short_reference: "Use full URLs instead of short references (`gitlab-org/gitlab#123`, " \
29
+ "`!123`, or `%%12.3`), as short references are displayed as plain text outside of GitLab"
30
30
  }
31
31
  )
32
32
  end
@@ -21,31 +21,54 @@ module Gitlab
21
21
  # match changed lines in files that match +filename_regex+. Used in `helper.changes_by_category`, `helper.changes`, and `helper.categories_for_file`.
22
22
  attr_accessor :files_to_category
23
23
 
24
+ # @!attribute custom_labels_for_categories
25
+ # @return [{String => String}] A hash of the form +{ category_name => label }+.
26
+ # Used in `helper.custom_labels_for_categories`.
27
+ attr_accessor :custom_labels_for_categories
28
+
29
+ # @!attribute disabled_roulette_categories
30
+ # @return [Array] indicating which categories would be disabled for the simple roulette. Default to `[]` (all categories are enabled)
31
+ attr_accessor :disabled_roulette_categories
32
+
33
+ # Rule: changes_size
24
34
  # @!attribute code_size_thresholds
25
35
  # @return [{ high: Integer, medium: Integer }] a hash of the form +{ high: 42, medium: 12 }+ where +:high+ is the lines changed threshold which triggers an error, and +:medium+ is the lines changed threshold which triggers a warning. Also, see +DEFAULT_CHANGES_SIZE_THRESHOLDS+ for the format of the hash.
26
36
  attr_accessor :code_size_thresholds
27
37
 
38
+ # Rule: commit_messages
28
39
  # @!attribute max_commits_count
29
40
  # @return [Integer] the maximum number of allowed non-squashed/non-fixup commits for a given MR. A warning is triggered if the MR has more commits.
30
41
  attr_accessor :max_commits_count
31
42
 
32
- # @!attribute disabled_roulette_categories
33
- # @return [Array] indicating which categories would be disabled for the simple roulette. Default to `[]` (all categories are enabled)
34
- attr_accessor :disabled_roulette_categories
43
+ # Rule: duo_code_review
44
+ # @!attribute duo_code_review
45
+ # @return [Symbol] whether a review from GitLab Duo Code is `:mandatory` or `:optional`. Default to `:optional`.
46
+ attr_accessor :duo_code_review
35
47
 
48
+ # Rule: simple_roulette
36
49
  # @!attribute included_optional_codeowners_sections_for_roulette
37
50
  # @return [Array] indicating which optional codeowners sections should be included in roulette. Default to `[]`.
38
51
  attr_accessor :included_optional_codeowners_sections_for_roulette
39
52
 
53
+ # Rule: simple_roulette
40
54
  # @!attribute excluded_required_codeowners_sections_for_roulette
41
55
  # @return [Array] indicating which required codeowners sections should be excluded from roulette. Default to `[]`.
42
56
  attr_accessor :excluded_required_codeowners_sections_for_roulette
43
57
 
58
+ # @!attribute auto_assign_for_roulette_roles
59
+ # @return [Array<Symbol>] which roles to auto assign as reviewers, given roulette recommendations (:reviewer, :maintainer, or both). If empty, auto-assignment is disabled. Default to `[]`.
60
+ attr_accessor :auto_assign_for_roulette_roles
61
+
62
+ # @!attribute auto_assign_for_roulette_labels
63
+ # @return [Array<String>] MR labels that allow auto reviewer assignment. If empty, applies to all MRs, provided :auto_assign_for_roulette_roles is not empty. Default to `[]`.
64
+ attr_accessor :auto_assign_for_roulette_labels
65
+
44
66
  DEFAULT_CHANGES_SIZE_THRESHOLDS = { high: 2_000, medium: 500 }.freeze
45
67
  DEFAULT_COMMIT_MESSAGES_MAX_COMMITS_COUNT = 10
46
68
 
47
69
  def initialize
48
70
  @files_to_category = {}
71
+ @custom_labels_for_categories = {}
49
72
  @project_root = nil
50
73
  @project_name = ENV["CI_PROJECT_NAME"]
51
74
  @ci_only_rules = []
@@ -54,6 +77,9 @@ module Gitlab
54
77
  @disabled_roulette_categories = []
55
78
  @included_optional_codeowners_sections_for_roulette = []
56
79
  @excluded_required_codeowners_sections_for_roulette = []
80
+ @auto_assign_for_roulette_roles = []
81
+ @auto_assign_for_roulette_labels = []
82
+ @duo_code_review = :optional
57
83
  end
58
84
  end
59
85
  end