class Danger::Helper
Common helper functions for our danger scripts.
Constants
- CATEGORY_LABELS
- RELEASE_TOOLS_BOT
Public Instance Methods
@return [Array<String>] a list of filenames added in this MR.
# File lib/danger/plugins/helper.rb, line 68 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
@example
# Considering these changes: # - A new_file.rb # - D deleted_file.rb # - M modified_file.rb # - R renamed_file_before.rb -> renamed_file_after.rb # it will return: #=> ['new_file.rb', 'modified_file.rb', 'renamed_file_after.rb']
@return [Array<String>] 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.
# File lib/danger/plugins/helper.rb, line 119 def all_changed_files Set.new .merge(added_files) .merge(modified_files) .merge(renamed_files.map { |x| x[:after] }) .subtract(renamed_files.map { |x| x[:before] }) .to_a .sort end
@param filename [String] A file name. @param categories [{Regexp => Array<Symbol>}, {Array<Regexp> => Array<Symbol>}] A hash of the form +{ filename_regex => categories, [filename_regex, changes_regex] => categories }+.
+filename_regex+ is the regex pattern to match file names. +changes_regex+ is the regex pattern to match changed lines in files that match +filename_regex+
@return [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 given +categories+ hash.
# File lib/danger/plugins/helper.rb, line 218 def categories_for_file(filename, categories) _, categories = categories.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 || :unknown) end
@param regex [Regexp] A Regexp to match against.
@return [Array<String>] changed files matching the given regex
.
# File lib/danger/plugins/helper.rb, line 381 def changed_files(regex) all_changed_files.grep(regex) end
@param filename [String] A file name for which we want the diff.
@example
# Considering changing a line in lib/gitlab/usage_data.rb, it will return: ["--- a/lib/gitlab/usage_data.rb", "+++ b/lib/gitlab/usage_data.rb", "+ # Test change", "- # Old change"]
@return [Array<String>] an array of changed lines in Git diff format.
# File lib/danger/plugins/helper.rb, line 140 def changed_lines(filename) diff = diff_for_file(filename) return [] unless diff diff.split("\n").select { |line| %r{^[+-]}.match?(line) } end
@param categories [{Regexp => Array<Symbol>}, {Array<Regexp> => Array<Symbol>}] A hash of the form +{ filename_regex => categories, [filename_regex, changes_regex] => categories }+.
+filename_regex+ is the regex pattern to match file names. +changes_regex+ is the regex pattern to match changed lines in files that match +filename_regex+
@return [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 given +categories+ hash.
# File lib/danger/plugins/helper.rb, line 187 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
@param categories [{Regexp => Array<Symbol>}, {Array<Regexp> => Array<Symbol>}] A hash of the form +{ filename_regex => categories, [filename_regex, changes_regex] => categories }+.
+filename_regex+ is the regex pattern to match file names. +changes_regex+ is the regex pattern to match changed lines in files that match +filename_regex+
@return [{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 given +categories+ hash.
# File lib/danger/plugins/helper.rb, line 175 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
@return [Boolean] whether a MR title includes “cherry-pick” or not.
# File lib/danger/plugins/helper.rb, line 308 def cherry_pick_mr? Gitlab::Dangerfiles::TitleLinting.has_cherry_pick_flag?(mr_title) end
@return [Boolean] whether we're in the CI context or not.
# File lib/danger/plugins/helper.rb, line 63 def ci? !gitlab_helper.nil? end
Allows to set specific rule's configuration by passing a block.
@yield [c] Yield a Gitlab::Dangerfiles::Config
object
@yieldparam [Gitlab::Dangerfiles::Config] The Gitlab::Dangerfiles::Config
object @yieldreturn [Gitlab::Dangerfiles::Config] The Gitlab::Dangerfiles::Config
object
@example
helper.config do |config| config.code_size_thresholds = { high: 42, medium: 12 } end
@return [Gitlab::Dangerfiles::Config]
# File lib/danger/plugins/helper.rb, line 40 def config (@config ||= Gitlab::Dangerfiles::Config.new).tap do |c| yield c if block_given? end end
@return [Array<String>] a list of filenames deleted in this MR.
# File lib/danger/plugins/helper.rb, line 97 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
@return [Boolean] whether a MR is a Draft or not.
# File lib/danger/plugins/helper.rb, line 296 def draft_mr? return false unless ci? gitlab.mr_json["work_in_progress"] end
@return [Array<String>] the group labels (i.e. +“group::*”+) set on the MR.
# File lib/danger/plugins/helper.rb, line 386 def group_label mr_labels.find { |label| label.start_with?("group::") } end
@return [Boolean] whether a MR has any CI-related changes (i.e. +“.gitlab-ci.yml”+ or +“.gitlab/ci/*”+) or not.
# File lib/danger/plugins/helper.rb, line 335 def has_ci_changes? changed_files(%r{\A(\.gitlab-ci\.yml|\.gitlab/ci/)}).any? end
Whether a MR has any database-scoped labels (i.e. +“database::*”+) set or not.
@return [Boolean]
# File lib/danger/plugins/helper.rb, line 330 def has_database_scoped_labels? mr_labels.any? { |label| label.start_with?("database::") } end
@example
<a href='https://gitlab.com/artsy/eigen/blob/561827e46167077b5e53515b4b7349b8ae04610b/file.txt'>file.txt</a>
@param [String, Array<String>] paths
A list of strings to convert to gitlab anchors
@param [Boolean] full_path
Shows the full path as the link's text, defaults to +true+.
@see danger.systems/reference.html Danger
reference where html_link
is described @see github.com/danger/danger/blob/eca19719d3e585fe1cc46bc5377f9aa955ebf609/lib/danger/danger_core/plugins/dangerfile_gitlab_plugin.rb#L216 Danger
reference where html_link
is implemented
@return [String] a list of HTML anchors for a file, or multiple files
# File lib/danger/plugins/helper.rb, line 58 def html_link(paths, full_path: true) ci? ? gitlab_helper.html_link(paths, full_path: full_path) : paths end
@param category [Symbol] A category.
@return [String] the GFM for a category label, making its best guess if it's not
a category we know about.
# File lib/danger/plugins/helper.rb, line 235 def label_for_category(category) CATEGORY_LABELS.fetch(category, "~#{category}") end
@param labels [Array<String>] An array of labels. @param sep [String] A separator.
@example
labels_list(["foo", "bar baz"], sep: "; ") # => '~"foo"; ~"bar baz"'
@return [String] the list of labels
ready for being used in a Markdown comment, separated by sep
.
# File lib/danger/plugins/helper.rb, line 356 def labels_list(labels, sep: ", ") labels.map { |label| %Q{~"#{label}"} }.join(sep) end
@param items [Array<String>] An array of items to transform into a bullet list.
@example
markdown_list(%w[foo bar]) # => * foo * bar
@return [String] a bullet list for the given items
. If there are more than 10 items, wrap the list in a +<details></details>+ block.
# File lib/danger/plugins/helper.rb, line 159 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
@return [Array<String>] a list of filenames modifier in this MR.
# File lib/danger/plugins/helper.rb, line 77 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
@param labels [Array<String>] An array of labels.
@return [Boolean] whether a MR has the given labels
set or not.
# File lib/danger/plugins/helper.rb, line 342 def mr_has_labels?(*labels) labels = labels.flatten.uniq (labels & mr_labels) == labels end
@return [String] +“”+ when not in the CI context, and the MR IID as a string otherwise.
# File lib/danger/plugins/helper.rb, line 240 def mr_iid return "" unless ci? gitlab_helper.mr_json["iid"].to_s end
@return [Array<String>] []
when not in the CI context, and the MR labels otherwise.
# File lib/danger/plugins/helper.rb, line 268 def mr_labels return [] unless ci? gitlab_helper.mr_labels end
@return [String] +`git rev-parse –abbrev-ref HEAD`+ when not in the CI context, and the MR source branch otherwise.
# File lib/danger/plugins/helper.rb, line 275 def mr_source_branch return `git rev-parse --abbrev-ref HEAD`.strip unless ci? gitlab_helper.mr_json["source_branch"] end
@return [String] +“”+ when not in the CI context, and the MR target branch otherwise.
# File lib/danger/plugins/helper.rb, line 282 def mr_target_branch return "" unless ci? gitlab_helper.mr_json["target_branch"] end
@return [String] +“”+ when not in the CI context, and the MR title otherwise.
# File lib/danger/plugins/helper.rb, line 254 def mr_title return "" unless ci? gitlab_helper.mr_json["title"] end
@return [String] +“”+ when not in the CI context, and the MR URL otherwise.
# File lib/danger/plugins/helper.rb, line 261 def mr_web_url return "" unless ci? gitlab_helper.mr_json["web_url"] end
@deprecated Use {#quick_action_label} instead.
# File lib/danger/plugins/helper.rb, line 361 def prepare_labels_for_mr(labels) quick_action_label(labels) end
@param labels [Array<String>] An array of labels.
@example
quick_action_label(["foo", "bar baz"]) # => '/label ~"foo" ~"bar baz"'
@return [String] a quick action to set the given
labels. Returns +“”+ if labels
is empty.
# File lib/danger/plugins/helper.rb, line 372 def quick_action_label(labels) return "" unless labels.any? "/label #{labels_list(labels, sep: " ")}" end
# File lib/danger/plugins/helper.rb, line 147 def release_automation? gitlab_helper&.mr_author == RELEASE_TOOLS_BOT end
@return [Array<String>] a list of filenames renamed in this MR.
# File lib/danger/plugins/helper.rb, line 86 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
@return [Boolean] whether a MR title includes “RUN ALL RSPEC” or not.
# File lib/danger/plugins/helper.rb, line 313 def run_all_rspec_mr? Gitlab::Dangerfiles::TitleLinting.has_run_all_rspec_flag?(mr_title) end
@return [Boolean] whether a MR title includes “RUN AS-IF-FOSS” or not.
# File lib/danger/plugins/helper.rb, line 318 def run_as_if_foss_mr? Gitlab::Dangerfiles::TitleLinting.has_run_as_if_foss_flag?(mr_title) end
@return [Boolean] whether a MR is opened in the security mirror or not.
# File lib/danger/plugins/helper.rb, line 303 def security_mr? mr_web_url.include?("/gitlab-org/security/") end
@return [Boolean] true
when not in the CI context, and whether the MR is set to be squashed otherwise.
# File lib/danger/plugins/helper.rb, line 289 def squash_mr? return true unless ci? gitlab.mr_json["squash"] end
@return [Boolean] whether a MR targets a stable branch or not.
# File lib/danger/plugins/helper.rb, line 323 def stable_branch? /\A\d+-\d+-stable-ee/i.match?(mr_target_branch) end
Private Instance Methods
Fetches MR changes from the API instead of Git (default).
@return [Array<Hash>, nil]
# File lib/danger/plugins/helper.rb, line 417 def changes_from_api return nil unless ci? return nil if defined?(@force_changes_from_git) @changes_from_api ||= gitlab_helper.mr_changes rescue # Fallback to the Git strategy in any case @force_changes_from_git = true nil end
@param filename [String] A filename for which we want the diff.
@return [String] the raw diff as a string for the given filename
.
# File lib/danger/plugins/helper.rb, line 405 def diff_for_file(filename) if changes_from_api changes_hash = changes_from_api.find { |file| file["new_path"] == filename } changes_hash["diff"] if changes_hash else git.diff_for_file(filename)&.patch end end
@return [Danger::RequestSources::GitLab, nil] the gitlab
helper, or nil
when it's not available.
# File lib/danger/plugins/helper.rb, line 393 def gitlab_helper # Unfortunately the following does not work: # - respond_to?(:gitlab) # - respond_to?(:gitlab, true) gitlab rescue NoMethodError nil end