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"', 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 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_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
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.
86 87 88 89 90 91 92 |
# File 'lib/danger/plugins/internal/helper.rb', line 86 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.
137 138 139 |
# File 'lib/danger/plugins/internal/helper.rb', line 137 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.
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/danger/plugins/internal/helper.rb', line 232 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
.
475 476 477 |
# File 'lib/danger/plugins/internal/helper.rb', line 475 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.
152 153 154 155 156 157 |
# File 'lib/danger/plugins/internal/helper.rb', line 152 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.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/danger/plugins/internal/helper.rb', line 201 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.
187 188 189 190 191 192 193 |
# File 'lib/danger/plugins/internal/helper.rb', line 187 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.
400 401 402 |
# File 'lib/danger/plugins/internal/helper.rb', line 400 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.
81 82 83 |
# File 'lib/danger/plugins/internal/helper.rb', line 81 def ci? !gitlab_helper.nil? end |
#config {|c| ... } ⇒ Gitlab::Dangerfiles::Config
Allows to set specific rule’s configuration by passing a block.
58 59 60 61 62 |
# File 'lib/danger/plugins/internal/helper.rb', line 58 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.
501 502 503 504 505 506 507 508 |
# File 'lib/danger/plugins/internal/helper.rb', line 501 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.
115 116 117 118 119 120 121 |
# File 'lib/danger/plugins/internal/helper.rb', line 115 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.
384 385 386 387 388 |
# File 'lib/danger/plugins/internal/helper.rb', line 384 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.
480 481 482 |
# File 'lib/danger/plugins/internal/helper.rb', line 480 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.
429 430 431 |
# File 'lib/danger/plugins/internal/helper.rb', line 429 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.
424 425 426 |
# File 'lib/danger/plugins/internal/helper.rb', line 424 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.
76 77 78 |
# File 'lib/danger/plugins/internal/helper.rb', line 76 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.
252 253 254 255 256 257 258 259 260 |
# File 'lib/danger/plugins/internal/helper.rb', line 252 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
.
450 451 452 |
# File 'lib/danger/plugins/internal/helper.rb', line 450 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.
496 497 498 |
# File 'lib/danger/plugins/internal/helper.rb', line 496 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.
171 172 173 174 175 176 177 178 179 |
# File 'lib/danger/plugins/internal/helper.rb', line 171 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.
95 96 97 98 99 100 101 |
# File 'lib/danger/plugins/internal/helper.rb', line 95 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.
354 355 356 357 358 359 360 |
# File 'lib/danger/plugins/internal/helper.rb', line 354 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.
291 292 293 294 295 |
# File 'lib/danger/plugins/internal/helper.rb', line 291 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.
284 285 286 287 288 |
# File 'lib/danger/plugins/internal/helper.rb', line 284 def return `whoami`.strip unless ci? gitlab_helper. end |
#mr_description ⇒ String
Returns “” when not in the CI context, and the MR description otherwise.
312 313 314 315 316 |
# File 'lib/danger/plugins/internal/helper.rb', line 312 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.
436 437 438 439 440 |
# File 'lib/danger/plugins/internal/helper.rb', line 436 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.
277 278 279 280 281 |
# File 'lib/danger/plugins/internal/helper.rb', line 277 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.
333 334 335 336 337 |
# File 'lib/danger/plugins/internal/helper.rb', line 333 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.
326 327 328 329 330 |
# File 'lib/danger/plugins/internal/helper.rb', line 326 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.
298 299 300 301 302 |
# File 'lib/danger/plugins/internal/helper.rb', line 298 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.
340 341 342 343 344 |
# File 'lib/danger/plugins/internal/helper.rb', line 340 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.
263 264 265 266 267 |
# File 'lib/danger/plugins/internal/helper.rb', line 263 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.
347 348 349 350 351 |
# File 'lib/danger/plugins/internal/helper.rb', line 347 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.
270 271 272 273 274 |
# File 'lib/danger/plugins/internal/helper.rb', line 270 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.
305 306 307 308 309 |
# File 'lib/danger/plugins/internal/helper.rb', line 305 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.
319 320 321 322 323 |
# File 'lib/danger/plugins/internal/helper.rb', line 319 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.
455 456 457 |
# File 'lib/danger/plugins/internal/helper.rb', line 455 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.
466 467 468 469 470 |
# File 'lib/danger/plugins/internal/helper.rb', line 466 def quick_action_label(labels) return "" unless labels.any? "/label #{labels_list(labels, sep: " ")}" end |
#release_automation? ⇒ Boolean
159 160 161 |
# File 'lib/danger/plugins/internal/helper.rb', line 159 def release_automation? == RELEASE_TOOLS_BOT end |
#renamed_files ⇒ Array<String>
Returns a list of filenames renamed in this MR.
104 105 106 107 108 109 110 111 112 |
# File 'lib/danger/plugins/internal/helper.rb', line 104 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 ”.
368 369 370 371 372 373 374 |
# File 'lib/danger/plugins/internal/helper.rb', line 368 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.
405 406 407 |
# File 'lib/danger/plugins/internal/helper.rb', line 405 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.
410 411 412 |
# File 'lib/danger/plugins/internal/helper.rb', line 410 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.
391 392 393 |
# File 'lib/danger/plugins/internal/helper.rb', line 391 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.
377 378 379 380 381 |
# File 'lib/danger/plugins/internal/helper.rb', line 377 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.
415 416 417 |
# File 'lib/danger/plugins/internal/helper.rb', line 415 def stable_branch? /\A\d+-\d+-stable-ee/i.match?(mr_target_branch) end |
#stable_branch_mr? ⇒ Boolean
395 396 397 |
# File 'lib/danger/plugins/internal/helper.rb', line 395 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.
485 486 487 |
# File 'lib/danger/plugins/internal/helper.rb', line 485 def stage_label mr_labels.find { |label| label.start_with?("devops::") } end |