Class: Danger::Helper
- Inherits:
-
Plugin
- Object
- Plugin
- Danger::Helper
- Defined in:
- lib/danger/plugins/internal/helper.rb
Overview
Common helper functions for our danger scripts.
Constant Summary collapse
- RELEASE_TOOLS_BOT =
"gitlab-release-tools-bot"
- CATEGORY_LABELS =
rubocop:disable Style/HashSyntax
{ docs: "~documentation", # Docs are reviewed along DevOps stages, so don't need roulette for now. none: "None", nil => "N/A", qa: "~QA", ux: "~UX", codeowners: '~"Code Owners"', test: "~test for `spec/features/*`", tooling: '~"maintenance::workflow" for tooling, Danger, and RuboCop', pipeline: '~"maintenance::pipelines" for CI', ci_template: '~"ci::templates"', analytics_instrumentation: '~"analytics instrumentation"', import_integrate_be: '~"group::import and integrate" (backend)', import_integrate_fe: '~"group::import and integrate" (frontend)', Authentication: '~"group::authentication"', Authorization: '~"group::authorization"', Compliance: '~"group::compliance"' }.freeze
- GITLAB_ORG_GROUP_ID =
rubocop:enable Style/HashSyntax
"9970"
- STABLE_BRANCH_REGEX =
%r{\A(?<version>\d+-\d+)-stable-ee\z}
- MR_REVERT_START_WITH =
/[Rr]evert /
Instance Method Summary collapse
-
#added_files ⇒ Array<String>
A list of filenames added in this MR.
-
#all_changed_files ⇒ Array<String>
A list of all files that have been added, modified or renamed.
-
#categories_for_file(filename, files_to_category = {}) ⇒ Array<Symbol>
The categories a file is in, e.g.,
[:frontend]
,[:backend]
, or %i[frontend tooling] using filename regex (filename_regex
) and specific change regex (changes_regex
) from the givencategories
hash. -
#changed_files(regex) ⇒ Array<String>
Changed files matching the given
regex
. -
#changed_lines(filename) ⇒ Array<String>
An array of changed lines in Git diff format.
-
#changes(categories = []) ⇒ Gitlab::Dangerfiles::Changes
A
Gitlab::Dangerfiles::Changes
object that represents the changes of an MR using filename regex (filename_regex
) and specific change regex (changes_regex
) from the givencategories
hash. -
#changes_by_category(categories = []) ⇒ {Symbol => Array<String>}
A hash of the type { category1: [“file1”, “file2”], category2: [“file3”, “file4”] } using filename regex (
filename_regex
) and specific change regex (changes_regex
) from the givencategories
hash. -
#cherry_pick_mr? ⇒ Boolean
Whether a MR title includes “cherry-pick” or not.
-
#ci? ⇒ Boolean
Whether we’re in the CI context or not.
-
#config {|c| ... } ⇒ Gitlab::Dangerfiles::Config
Allows to set specific rule’s configuration by passing a block.
-
#current_milestone ⇒ Hash
The current API milestone object or
nil
if run in dry-run mode. -
#deleted_files ⇒ Array<String>
A list of filenames deleted in this MR.
-
#draft_mr? ⇒ Boolean
Whether a MR is a Draft or not.
-
#group_label ⇒ String
The group label (i.e. “group::*”) set on the MR.
-
#has_ci_changes? ⇒ Boolean
Whether a MR has any CI-related changes (i.e. “.gitlab-ci.yml” or “.gitlab/ci/*”) or not.
-
#has_scoped_label_with_scope?(scope) ⇒ Boolean
Whether a MR has a scoped label with the given scope set or not.
-
#html_link(paths, full_path: true) ⇒ String
A list of HTML anchors for a file, or multiple files.
-
#label_for_category(category) ⇒ String
The GFM for a category label, making its best guess if it’s not a category we know about.
-
#labels_list(labels, sep: ", ") ⇒ String
The list of
labels
ready for being used in a Markdown comment, separated bysep
. -
#labels_to_add ⇒ Array<String>
Accessor for storing labels to add so that other rules can check if labels will be added after Danger has evaluated all the rules.
-
#markdown_list(items) ⇒ String
A bullet list for the given
items
. -
#modified_files ⇒ Array<String>
A list of filenames modifier in this MR.
-
#mr_approval_state ⇒ Hash
{} when not in the CI context, and the merge request approval state otherwise.
-
#mr_assignees ⇒ Array<Hash>
[]
when not in the CI context, and the MR assignees otherwise. -
#mr_author ⇒ String
‘whoami` when not in the CI context, and the MR author username otherwise.
-
#mr_description ⇒ String
“” when not in the CI context, and the MR description otherwise.
-
#mr_has_labels?(*labels) ⇒ Boolean
Whether a MR has the given
labels
set or not. -
#mr_iid ⇒ String
“” when not in the CI context, and the MR IID as a string otherwise.
-
#mr_labels ⇒ Array<String>
[]
when not in the CI context, and the MR labels otherwise. -
#mr_milestone ⇒ Hash?
nil
when not in the CI context, and the MR milestone otherwise. -
#mr_source_branch ⇒ String
‘git rev-parse –abbrev-ref HEAD` when not in the CI context, and the MR source branch otherwise.
-
#mr_source_project_id ⇒ String
“” when not in the CI context, and the MR Source Project ID as a string otherwise.
-
#mr_target_branch ⇒ String
“” when not in the CI context, and the MR target branch otherwise.
-
#mr_target_project_id ⇒ String
“” when not in the CI context, and the MR Target Project ID as a string otherwise.
-
#mr_title ⇒ String
“” when not in the CI context, and the MR title otherwise.
-
#mr_web_url ⇒ String
“” when not in the CI context, and the MR URL otherwise.
-
#prepare_labels_for_mr(labels) ⇒ Object
deprecated
Deprecated.
Use #quick_action_label instead.
-
#quick_action_label(labels) ⇒ String
A quick action to set the
given
labels. - #release_automation? ⇒ Boolean
-
#renamed_files ⇒ Array<String>
A list of filenames renamed in this MR.
-
#revert_mr? ⇒ Boolean
When API token is available matches MR title to start with “Revert ” or “revert ”.
-
#run_all_rspec_mr? ⇒ Boolean
Whether a MR title includes “RUN ALL RSPEC” or not.
-
#run_as_if_foss_mr? ⇒ Boolean
Whether a MR title includes “RUN AS-IF-FOSS” or not.
-
#security_mr? ⇒ Boolean
Whether a MR is opened in the security mirror or not.
-
#squash_mr? ⇒ Boolean
true
when not in the CI context, and whether the MR is set to be squashed otherwise. -
#stable_branch? ⇒ Boolean
Whether a MR targets a stable branch or not.
- #stable_branch_mr? ⇒ Boolean
-
#stage_label ⇒ String
The stage label (i.e. “devops::*”) set on the MR.
Instance Method Details
#added_files ⇒ Array<String>
Returns a list of filenames added in this MR.
81 82 83 84 85 86 87 |
# File 'lib/danger/plugins/internal/helper.rb', line 81 def added_files @added_files ||= if changes_from_api changes_from_api.select { |file| file["new_file"] }.map { |file| file["new_path"] } else git.added_files.to_a end end |
#all_changed_files ⇒ Array<String>
Returns a list of all files that have been added, modified or renamed. modified_files
might contain paths that already have been renamed, so we need to remove them from the list.
132 133 134 |
# File 'lib/danger/plugins/internal/helper.rb', line 132 def all_changed_files changes.files - changes.deleted.files - changes.renamed_before.files end |
#categories_for_file(filename, files_to_category = {}) ⇒ Array<Symbol>
Returns the categories a file is in, e.g., [:frontend]
, [:backend]
, or %i[frontend tooling] using filename regex (filename_regex
) and specific change regex (changes_regex
) from the given categories
hash.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/danger/plugins/internal/helper.rb', line 225 def categories_for_file(filename, files_to_category = {}) files_to_category = Array(files_to_category).compact files_to_category = helper.config.files_to_category if files_to_category.empty? _, categories = files_to_category.find do |key, _| filename_regex, changes_regex = Array(key) found = filename_regex.match?(filename) found &&= changed_lines(filename).any? { |changed_line| changes_regex.match?(changed_line) } if changes_regex found end Array(categories || :none) end |
#changed_files(regex) ⇒ Array<String>
Returns changed files matching the given regex
.
461 462 463 |
# File 'lib/danger/plugins/internal/helper.rb', line 461 def changed_files(regex) all_changed_files.grep(regex) end |
#changed_lines(filename) ⇒ Array<String>
Returns an array of changed lines in Git diff format.
147 148 149 150 151 152 |
# File 'lib/danger/plugins/internal/helper.rb', line 147 def changed_lines(filename) diff = diff_for_file(filename) return [] unless diff diff.split("\n").select { |line| %r{^[+-]}.match?(line) } end |
#changes(categories = []) ⇒ Gitlab::Dangerfiles::Changes
Returns a Gitlab::Dangerfiles::Changes
object that represents the changes of an MR using filename regex (filename_regex
) and specific change regex (changes_regex
) from the given categories
hash.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/danger/plugins/internal/helper.rb', line 194 def changes(categories = []) Gitlab::Dangerfiles::Changes.new([]).tap do |changes| added_files.each do |file| categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :added, category) } end modified_files.each do |file| categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :modified, category) } end deleted_files.each do |file| categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :deleted, category) } end renamed_files.map { |x| x[:before] }.each do |file| categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :renamed_before, category) } end renamed_files.map { |x| x[:after] }.each do |file| categories_for_file(file, categories).each { |category| changes << Gitlab::Dangerfiles::Change.new(file, :renamed_after, category) } end end end |
#changes_by_category(categories = []) ⇒ {Symbol => Array<String>}
Returns a hash of the type { category1: [“file1”, “file2”], category2: [“file3”, “file4”] } using filename regex (filename_regex
) and specific change regex (changes_regex
) from the given categories
hash.
182 183 184 185 186 |
# File 'lib/danger/plugins/internal/helper.rb', line 182 def changes_by_category(categories = []) all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash| categories_for_file(file, categories).each { |category| hash[category] << file } end end |
#cherry_pick_mr? ⇒ Boolean
Returns whether a MR title includes “cherry-pick” or not.
386 387 388 |
# File 'lib/danger/plugins/internal/helper.rb', line 386 def cherry_pick_mr? Gitlab::Dangerfiles::TitleLinting.has_cherry_pick_flag?(mr_title) end |
#ci? ⇒ Boolean
Returns whether we’re in the CI context or not.
76 77 78 |
# File 'lib/danger/plugins/internal/helper.rb', line 76 def ci? !gitlab_helper.nil? end |
#config {|c| ... } ⇒ Gitlab::Dangerfiles::Config
Allows to set specific rule’s configuration by passing a block.
53 54 55 56 57 |
# File 'lib/danger/plugins/internal/helper.rb', line 53 def config (@config ||= Gitlab::Dangerfiles::Config.new).tap do |c| yield c if block_given? end end |
#current_milestone ⇒ Hash
Returns the current API milestone object or nil
if run in dry-run mode.
487 488 489 490 491 492 493 494 |
# File 'lib/danger/plugins/internal/helper.rb', line 487 def current_milestone return unless ci? @current_milestone ||= gitlab_helper.api.group_milestones(GITLAB_ORG_GROUP_ID, state: "active") .auto_paginate .select { |m| m.title.match?(/\A\d+\.\d+\z/) && !m.expired && m.start_date && m.due_date } .min_by(&:start_date) end |
#deleted_files ⇒ Array<String>
Returns a list of filenames deleted in this MR.
110 111 112 113 114 115 116 |
# File 'lib/danger/plugins/internal/helper.rb', line 110 def deleted_files @deleted_files ||= if changes_from_api changes_from_api.select { |file| file["deleted_file"] }.map { |file| file["new_path"] } else git.deleted_files.to_a end end |
#draft_mr? ⇒ Boolean
Returns whether a MR is a Draft or not.
370 371 372 373 374 |
# File 'lib/danger/plugins/internal/helper.rb', line 370 def draft_mr? return false unless ci? gitlab.mr_json["work_in_progress"] end |
#group_label ⇒ String
Returns the group label (i.e. “group::*”) set on the MR.
466 467 468 |
# File 'lib/danger/plugins/internal/helper.rb', line 466 def group_label mr_labels.find { |label| label.start_with?("group::") } end |
#has_ci_changes? ⇒ Boolean
Returns whether a MR has any CI-related changes (i.e. “.gitlab-ci.yml” or “.gitlab/ci/*”) or not.
415 416 417 |
# File 'lib/danger/plugins/internal/helper.rb', line 415 def has_ci_changes? changed_files(%r{\A(\.gitlab-ci\.yml|\.gitlab/ci/)}).any? end |
#has_scoped_label_with_scope?(scope) ⇒ Boolean
Whether a MR has a scoped label with the given scope set or not.
410 411 412 |
# File 'lib/danger/plugins/internal/helper.rb', line 410 def has_scoped_label_with_scope?(scope) mr_labels.any? { |label| label.start_with?("#{scope}::") } end |
#html_link(paths, full_path: true) ⇒ String
Returns a list of HTML anchors for a file, or multiple files.
71 72 73 |
# File 'lib/danger/plugins/internal/helper.rb', line 71 def html_link(paths, full_path: true) ci? ? gitlab_helper.html_link(paths, full_path: full_path) : paths end |
#label_for_category(category) ⇒ String
Returns the GFM for a category label, making its best guess if it’s not a category we know about.
245 246 247 248 249 250 251 252 253 |
# File 'lib/danger/plugins/internal/helper.rb', line 245 def label_for_category(category) CATEGORY_LABELS[category] || if category.start_with?("`") category.to_s else %Q{~"#{category}"} end end |
#labels_list(labels, sep: ", ") ⇒ String
Returns the list of labels
ready for being used in a Markdown comment, separated by sep
.
436 437 438 |
# File 'lib/danger/plugins/internal/helper.rb', line 436 def labels_list(labels, sep: ", ") labels.map { |label| %Q{~"#{label}"} }.join(sep) end |
#labels_to_add ⇒ Array<String>
Accessor for storing labels to add so that other rules can check if labels will be added after Danger has evaluated all the rules. For instance, a rule might require a specific label to be set, but another rule could add this label itself. Without this method, the first rule wouldn’t know that the label would be applied and would ask for it anyway.
482 483 484 |
# File 'lib/danger/plugins/internal/helper.rb', line 482 def labels_to_add @labels_to_add ||= [] end |
#markdown_list(items) ⇒ String
Returns a bullet list for the given items
. If there are more than 10 items, wrap the list in a <details></details> block.
166 167 168 169 170 171 172 173 174 |
# File 'lib/danger/plugins/internal/helper.rb', line 166 def markdown_list(items) list = items.map { |item| "* `#{item}`" }.join("\n") if items.size > 10 "\n<details>\n\n#{list}\n\n</details>\n" else list end end |
#modified_files ⇒ Array<String>
Returns a list of filenames modifier in this MR.
90 91 92 93 94 95 96 |
# File 'lib/danger/plugins/internal/helper.rb', line 90 def modified_files @modified_files ||= if changes_from_api changes_from_api.select { |file| !file["new_file"] && !file["deleted_file"] && !file["renamed_file"] }.map { |file| file["new_path"] } else git.modified_files.to_a end end |
#mr_approval_state ⇒ Hash
Returns {} when not in the CI context, and the merge request approval state otherwise.
340 341 342 343 344 345 346 |
# File 'lib/danger/plugins/internal/helper.rb', line 340 def mr_approval_state return {} unless ci? gitlab_helper.api.merge_request_approval_state( mr_target_project_id, mr_iid ) end |
#mr_assignees ⇒ Array<Hash>
Returns []
when not in the CI context, and the MR assignees otherwise.
284 285 286 287 288 |
# File 'lib/danger/plugins/internal/helper.rb', line 284 def mr_assignees return [] unless ci? gitlab_helper.mr_json["assignees"] end |
#mr_author ⇒ String
Returns ‘whoami` when not in the CI context, and the MR author username otherwise.
277 278 279 280 281 |
# File 'lib/danger/plugins/internal/helper.rb', line 277 def return `whoami`.strip unless ci? gitlab_helper. end |
#mr_description ⇒ String
Returns “” when not in the CI context, and the MR description otherwise.
298 299 300 301 302 |
# File 'lib/danger/plugins/internal/helper.rb', line 298 def mr_description return "" unless ci? gitlab_helper.mr_body end |
#mr_has_labels?(*labels) ⇒ Boolean
Returns whether a MR has the given labels
set or not.
422 423 424 425 426 |
# File 'lib/danger/plugins/internal/helper.rb', line 422 def mr_has_labels?(*labels) labels = labels.flatten.uniq (labels & mr_labels) == labels end |
#mr_iid ⇒ String
Returns “” when not in the CI context, and the MR IID as a string otherwise.
270 271 272 273 274 |
# File 'lib/danger/plugins/internal/helper.rb', line 270 def mr_iid return "" unless ci? gitlab_helper.mr_json["iid"].to_s end |
#mr_labels ⇒ Array<String>
Returns []
when not in the CI context, and the MR labels otherwise.
319 320 321 322 323 |
# File 'lib/danger/plugins/internal/helper.rb', line 319 def mr_labels return [] unless ci? (gitlab_helper.mr_labels + labels_to_add).uniq end |
#mr_milestone ⇒ Hash?
Returns nil
when not in the CI context, and the MR milestone otherwise.
312 313 314 315 316 |
# File 'lib/danger/plugins/internal/helper.rb', line 312 def mr_milestone return unless ci? gitlab_helper.mr_json["milestone"] end |
#mr_source_branch ⇒ String
Returns ‘git rev-parse –abbrev-ref HEAD` when not in the CI context, and the MR source branch otherwise.
326 327 328 329 330 |
# File 'lib/danger/plugins/internal/helper.rb', line 326 def mr_source_branch return `git rev-parse --abbrev-ref HEAD`.strip unless ci? gitlab_helper.mr_json["source_branch"] end |
#mr_source_project_id ⇒ String
Returns “” when not in the CI context, and the MR Source Project ID as a string otherwise.
256 257 258 259 260 |
# File 'lib/danger/plugins/internal/helper.rb', line 256 def mr_source_project_id return "" unless ci? gitlab_helper.mr_json["source_project_id"].to_s end |
#mr_target_branch ⇒ String
Returns “” when not in the CI context, and the MR target branch otherwise.
333 334 335 336 337 |
# File 'lib/danger/plugins/internal/helper.rb', line 333 def mr_target_branch return "" unless ci? gitlab_helper.mr_json["target_branch"] end |
#mr_target_project_id ⇒ String
Returns “” when not in the CI context, and the MR Target Project ID as a string otherwise.
263 264 265 266 267 |
# File 'lib/danger/plugins/internal/helper.rb', line 263 def mr_target_project_id return "" unless ci? gitlab_helper.mr_json["target_project_id"].to_s end |
#mr_title ⇒ String
Returns “” when not in the CI context, and the MR title otherwise.
291 292 293 294 295 |
# File 'lib/danger/plugins/internal/helper.rb', line 291 def mr_title return "" unless ci? gitlab_helper.mr_json["title"] end |
#mr_web_url ⇒ String
Returns “” when not in the CI context, and the MR URL otherwise.
305 306 307 308 309 |
# File 'lib/danger/plugins/internal/helper.rb', line 305 def mr_web_url return "" unless ci? gitlab_helper.mr_json["web_url"] end |
#prepare_labels_for_mr(labels) ⇒ Object
Use #quick_action_label instead.
441 442 443 |
# File 'lib/danger/plugins/internal/helper.rb', line 441 def prepare_labels_for_mr(labels) quick_action_label(labels) end |
#quick_action_label(labels) ⇒ String
Returns a quick action to set the given
labels. Returns “” if labels
is empty.
452 453 454 455 456 |
# File 'lib/danger/plugins/internal/helper.rb', line 452 def quick_action_label(labels) return "" unless labels.any? "/label #{labels_list(labels, sep: " ")}" end |
#release_automation? ⇒ Boolean
154 155 156 |
# File 'lib/danger/plugins/internal/helper.rb', line 154 def release_automation? == RELEASE_TOOLS_BOT end |
#renamed_files ⇒ Array<String>
Returns a list of filenames renamed in this MR.
99 100 101 102 103 104 105 106 107 |
# File 'lib/danger/plugins/internal/helper.rb', line 99 def renamed_files @renamed_files ||= if changes_from_api changes_from_api.select { |file| file["renamed_file"] }.each_with_object([]) do |file, memo| memo << { before: file["old_path"], after: file["new_path"] } end else git.renamed_files.to_a end end |
#revert_mr? ⇒ Boolean
When API token is available matches MR title to start with “Revert ” or “revert ”. Otherwise, matches if the single commit’s message starts with “Revert ” or “revert ”.
354 355 356 357 358 359 360 |
# File 'lib/danger/plugins/internal/helper.rb', line 354 def revert_mr? if ci? mr_title.start_with?(MR_REVERT_START_WITH) else git.commits.size == 1 && git.commits.first..start_with?(MR_REVERT_START_WITH) end end |
#run_all_rspec_mr? ⇒ Boolean
Returns whether a MR title includes “RUN ALL RSPEC” or not.
391 392 393 |
# File 'lib/danger/plugins/internal/helper.rb', line 391 def run_all_rspec_mr? Gitlab::Dangerfiles::TitleLinting.has_run_all_rspec_flag?(mr_title) end |
#run_as_if_foss_mr? ⇒ Boolean
Returns whether a MR title includes “RUN AS-IF-FOSS” or not.
396 397 398 |
# File 'lib/danger/plugins/internal/helper.rb', line 396 def run_as_if_foss_mr? Gitlab::Dangerfiles::TitleLinting.has_run_as_if_foss_flag?(mr_title) end |
#security_mr? ⇒ Boolean
Returns whether a MR is opened in the security mirror or not.
377 378 379 |
# File 'lib/danger/plugins/internal/helper.rb', line 377 def security_mr? mr_web_url.include?("/gitlab-org/security/") end |
#squash_mr? ⇒ Boolean
Returns true
when not in the CI context, and whether the MR is set to be squashed otherwise.
363 364 365 366 367 |
# File 'lib/danger/plugins/internal/helper.rb', line 363 def squash_mr? return true unless ci? gitlab.mr_json["squash"] end |
#stable_branch? ⇒ Boolean
Returns whether a MR targets a stable branch or not.
401 402 403 |
# File 'lib/danger/plugins/internal/helper.rb', line 401 def stable_branch? /\A\d+-\d+-stable-ee/i.match?(mr_target_branch) end |
#stable_branch_mr? ⇒ Boolean
381 382 383 |
# File 'lib/danger/plugins/internal/helper.rb', line 381 def stable_branch_mr? !!mr_target_branch.match(STABLE_BRANCH_REGEX) && !security_mr? end |
#stage_label ⇒ String
Returns the stage label (i.e. “devops::*”) set on the MR.
471 472 473 |
# File 'lib/danger/plugins/internal/helper.rb', line 471 def stage_label mr_labels.find { |label| label.start_with?("devops::") } end |