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"', Authentication: '~"group::authentication"', Authorization: '~"group::authorization"', Compliance: '~"group::compliance"', Verify: '~"devops::verify"', "AST:Composition Analysis": '~"group::composition analysis"', "AST:Dynamic Analysis": '~"group::dynamic analysis"', "AST:Secret Detection": '~"group::secret detection"', "AST:Static Analysis": '~"group::static analysis"' }.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 givencategorieshash. -
#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::Changesobject that represents the changes of an MR using filename regex (filename_regex) and specific change regex (changes_regex) from the givencategorieshash. -
#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 givencategorieshash. -
#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
nilif 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
labelsready 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
labelsset 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?
nilwhen not in the CI context, and the MR milestone otherwise. -
#mr_reviewers ⇒ Array<Hash>
[]when not in the CI context, and the MR reviewers 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
givenlabels. - #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
truewhen 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.
84 85 86 87 88 89 90 |
# File 'lib/danger/plugins/internal/helper.rb', line 84 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.
135 136 137 |
# File 'lib/danger/plugins/internal/helper.rb', line 135 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.
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/danger/plugins/internal/helper.rb', line 230 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.
473 474 475 |
# File 'lib/danger/plugins/internal/helper.rb', line 473 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.
150 151 152 153 154 155 |
# File 'lib/danger/plugins/internal/helper.rb', line 150 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.
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/danger/plugins/internal/helper.rb', line 199 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.
185 186 187 188 189 190 191 |
# File 'lib/danger/plugins/internal/helper.rb', line 185 def changes_by_category(categories = []) changed_and_deleted_files = all_changed_files + changes.deleted.files changed_and_deleted_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.
398 399 400 |
# File 'lib/danger/plugins/internal/helper.rb', line 398 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.
79 80 81 |
# File 'lib/danger/plugins/internal/helper.rb', line 79 def ci? !gitlab_helper.nil? end |
#config {|c| ... } ⇒ Gitlab::Dangerfiles::Config
Allows to set specific rule’s configuration by passing a block.
56 57 58 59 60 |
# File 'lib/danger/plugins/internal/helper.rb', line 56 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.
499 500 501 502 503 504 505 506 |
# File 'lib/danger/plugins/internal/helper.rb', line 499 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.
113 114 115 116 117 118 119 |
# File 'lib/danger/plugins/internal/helper.rb', line 113 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.
382 383 384 385 386 |
# File 'lib/danger/plugins/internal/helper.rb', line 382 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.
478 479 480 |
# File 'lib/danger/plugins/internal/helper.rb', line 478 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.
427 428 429 |
# File 'lib/danger/plugins/internal/helper.rb', line 427 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.
422 423 424 |
# File 'lib/danger/plugins/internal/helper.rb', line 422 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.
74 75 76 |
# File 'lib/danger/plugins/internal/helper.rb', line 74 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.
250 251 252 253 254 255 256 257 258 |
# File 'lib/danger/plugins/internal/helper.rb', line 250 def label_for_category(category) helper.config.custom_labels_for_categories[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.
448 449 450 |
# File 'lib/danger/plugins/internal/helper.rb', line 448 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.
494 495 496 |
# File 'lib/danger/plugins/internal/helper.rb', line 494 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.
169 170 171 172 173 174 175 176 177 |
# File 'lib/danger/plugins/internal/helper.rb', line 169 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.
93 94 95 96 97 98 99 |
# File 'lib/danger/plugins/internal/helper.rb', line 93 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.
352 353 354 355 356 357 358 |
# File 'lib/danger/plugins/internal/helper.rb', line 352 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.
289 290 291 292 293 |
# File 'lib/danger/plugins/internal/helper.rb', line 289 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.
282 283 284 285 286 |
# File 'lib/danger/plugins/internal/helper.rb', line 282 def return `whoami`.strip unless ci? gitlab_helper. end |
#mr_description ⇒ String
Returns “” when not in the CI context, and the MR description otherwise.
310 311 312 313 314 |
# File 'lib/danger/plugins/internal/helper.rb', line 310 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.
434 435 436 437 438 |
# File 'lib/danger/plugins/internal/helper.rb', line 434 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.
275 276 277 278 279 |
# File 'lib/danger/plugins/internal/helper.rb', line 275 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.
331 332 333 334 335 |
# File 'lib/danger/plugins/internal/helper.rb', line 331 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.
324 325 326 327 328 |
# File 'lib/danger/plugins/internal/helper.rb', line 324 def mr_milestone return unless ci? gitlab_helper.mr_json["milestone"] end |
#mr_reviewers ⇒ Array<Hash>
Returns [] when not in the CI context, and the MR reviewers otherwise.
296 297 298 299 300 |
# File 'lib/danger/plugins/internal/helper.rb', line 296 def mr_reviewers return [] unless ci? gitlab_helper.mr_json["reviewers"] end |
#mr_source_branch ⇒ String
Returns ‘git rev-parse –abbrev-ref HEAD` when not in the CI context, and the MR source branch otherwise.
338 339 340 341 342 |
# File 'lib/danger/plugins/internal/helper.rb', line 338 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.
261 262 263 264 265 |
# File 'lib/danger/plugins/internal/helper.rb', line 261 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.
345 346 347 348 349 |
# File 'lib/danger/plugins/internal/helper.rb', line 345 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.
268 269 270 271 272 |
# File 'lib/danger/plugins/internal/helper.rb', line 268 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.
303 304 305 306 307 |
# File 'lib/danger/plugins/internal/helper.rb', line 303 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.
317 318 319 320 321 |
# File 'lib/danger/plugins/internal/helper.rb', line 317 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.
453 454 455 |
# File 'lib/danger/plugins/internal/helper.rb', line 453 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.
464 465 466 467 468 |
# File 'lib/danger/plugins/internal/helper.rb', line 464 def quick_action_label(labels) return "" unless labels.any? "/label #{labels_list(labels, sep: " ")}" end |
#release_automation? ⇒ Boolean
157 158 159 |
# File 'lib/danger/plugins/internal/helper.rb', line 157 def release_automation? == RELEASE_TOOLS_BOT end |
#renamed_files ⇒ Array<String>
Returns a list of filenames renamed in this MR.
102 103 104 105 106 107 108 109 110 |
# File 'lib/danger/plugins/internal/helper.rb', line 102 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 ”.
366 367 368 369 370 371 372 |
# File 'lib/danger/plugins/internal/helper.rb', line 366 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.
403 404 405 |
# File 'lib/danger/plugins/internal/helper.rb', line 403 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.
408 409 410 |
# File 'lib/danger/plugins/internal/helper.rb', line 408 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.
389 390 391 |
# File 'lib/danger/plugins/internal/helper.rb', line 389 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.
375 376 377 378 379 |
# File 'lib/danger/plugins/internal/helper.rb', line 375 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.
413 414 415 |
# File 'lib/danger/plugins/internal/helper.rb', line 413 def stable_branch? /\A\d+-\d+-stable-ee/i.match?(mr_target_branch) end |
#stable_branch_mr? ⇒ Boolean
393 394 395 |
# File 'lib/danger/plugins/internal/helper.rb', line 393 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.
483 484 485 |
# File 'lib/danger/plugins/internal/helper.rb', line 483 def stage_label mr_labels.find { |label| label.start_with?("devops::") } end |