class Dependabot::PullRequestCreator::PrNamePrefixer

Constants

ANGULAR_PREFIXES
ESLINT_PREFIXES
GITMOJI_PREFIXES

Attributes

commit_message_options[R]
credentials[R]
dependencies[R]
source[R]

Public Class Methods

new(source:, dependencies:, credentials:, security_fix: false, commit_message_options: {}) click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 30
def initialize(source:, dependencies:, credentials:, security_fix: false,
               commit_message_options: {})
  @dependencies           = dependencies
  @source                 = source
  @credentials            = credentials
  @security_fix           = security_fix
  @commit_message_options = commit_message_options
end

Public Instance Methods

capitalize_first_word?() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 45
def capitalize_first_word?
  return !commit_message_options[:prefix]&.strip&.match?(/\A[a-z]/) if commit_message_options.key?(:prefix)

  return capitalise_first_word_from_last_dependabot_commit_style if last_dependabot_commit_style

  capitalise_first_word_from_previous_commits
end
pr_name_prefix() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 39
def pr_name_prefix
  prefix = commit_prefix.to_s
  prefix += security_prefix if security_fix?
  prefix.gsub("⬆️ 🔒", "⬆️🔒")
end

Private Instance Methods

angular_commit_prefix() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 218
def angular_commit_prefix
  raise "Not using angular commits!" unless using_angular_commit_messages?

  recent_commits_using_chore =
    recent_commit_messages.
    any? { |msg| msg.start_with?("chore", "Chore") }

  recent_commits_using_build =
    recent_commit_messages.
    any? { |msg| msg.start_with?("build", "Build") }

  commit_prefix =
    if recent_commits_using_chore && !recent_commits_using_build
      "chore"
    else
      "build"
    end

  commit_prefix = commit_prefix.capitalize if capitalize_angular_commit_prefix?

  commit_prefix
end
azure_client_for_source() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 428
def azure_client_for_source
  @azure_client_for_source ||=
    Dependabot::Clients::Azure.for_source(
      source: source,
      credentials: credentials
    )
end
azure_commit_author_email(commit) click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 403
def azure_commit_author_email(commit)
  commit.fetch("author").fetch("email", "")
end
bitbucket_client_for_source() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 436
def bitbucket_client_for_source
  @bitbucket_client_for_source ||=
    Dependabot::Clients::Bitbucket.for_source(
      source: source,
      credentials: credentials
    )
end
bitbucket_commit_author_email(commit) click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 407
def bitbucket_commit_author_email(commit)
  matches = commit.fetch("author").fetch("raw").match(/<(.*)>/)
  matches ? matches[1] : ""
end
build_commit_prefix_from_previous_commits() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 112
def build_commit_prefix_from_previous_commits
  if using_angular_commit_messages?
    "#{angular_commit_prefix}(#{scope}): "
  elsif using_eslint_commit_messages?
    # https://eslint.org/docs/developer-guide/contributing/pull-requests
    "Upgrade: "
  elsif using_gitmoji_commit_messages?
    "⬆️ "
  elsif using_prefixed_commit_messages?
    "build(#{scope}): "
  end
end
capitalise_first_word_from_last_dependabot_commit_style() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 129
def capitalise_first_word_from_last_dependabot_commit_style
  case last_dependabot_commit_style
  when :gitmoji then true
  when :conventional_prefix, :conventional_prefix_with_scope
    last_dependabot_commit_message.match?(/: (\[[Ss]ecurity\] )?(B|U)/)
  else raise "Unknown commit style #{last_dependabot_commit_style}"
  end
end
capitalise_first_word_from_previous_commits() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 138
def capitalise_first_word_from_previous_commits
  if using_angular_commit_messages? || using_eslint_commit_messages?
    prefixes = ANGULAR_PREFIXES + ESLINT_PREFIXES
    semantic_msgs = recent_commit_messages.select do |message|
      prefixes.any? { |pre| message.match?(/#{pre}[:(]/i) }
    end

    return true if semantic_msgs.all? { |m| m.match?(/:\s+\[?[A-Z]/) }
    return false if semantic_msgs.all? { |m| m.match?(/:\s+\[?[a-z]/) }
  end

  !commit_prefix&.match(/\A[a-z]/)
end
capitalize_angular_commit_prefix?() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 241
def capitalize_angular_commit_prefix?
  semantic_messages = recent_commit_messages.select do |message|
    ANGULAR_PREFIXES.any? { |pre| message.match?(/#{pre}[:(]/i) }
  end

  return last_dependabot_commit_message&.start_with?(/[A-Z]/) if semantic_messages.none?

  capitalized_msgs = semantic_messages.
                     select { |m| m.start_with?(/[A-Z]/) }
  capitalized_msgs.count.to_f / semantic_messages.count > 0.5
end
codecommit_client_for_source() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 444
def codecommit_client_for_source
  @codecommit_client_for_source ||=
    Dependabot::Clients::CodeCommit.for_source(
      source: source,
      credentials: credentials
    )
end
commit_prefix() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 61
def commit_prefix
  # If a preferred prefix has been explicitly provided, use it
  return prefix_from_explicitly_provided_details if commit_message_options.key?(:prefix)

  # Otherwise, if there is a previous Dependabot commit and it used a
  # known style, use that as our model for subsequent commits
  return prefix_for_last_dependabot_commit_style if last_dependabot_commit_style

  # Otherwise we need to detect the user's preferred style from the
  # existing commits on their repo
  build_commit_prefix_from_previous_commits
end
dependabot_email() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 274
def dependabot_email
  "support@dependabot.com"
end
explicitly_provided_prefix_string() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 84
def explicitly_provided_prefix_string
  raise "No explicitly provided prefix!" unless commit_message_options.key?(:prefix)

  if dependencies.any?(&:production?)
    commit_message_options[:prefix].to_s
  elsif commit_message_options.key?(:prefix_development)
    commit_message_options[:prefix_development].to_s
  else
    commit_message_options[:prefix].to_s
  end
end
github_client_for_source() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 412
def github_client_for_source
  @github_client_for_source ||=
    Dependabot::Clients::GithubWithRetries.for_source(
      source: source,
      credentials: credentials
    )
end
gitlab_client_for_source() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 420
def gitlab_client_for_source
  @gitlab_client_for_source ||=
    Dependabot::Clients::GitlabWithRetries.for_source(
      source: source,
      credentials: credentials
    )
end
last_azure_dependabot_commit_message() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 373
def last_azure_dependabot_commit_message
  @recent_azure_commit_messages ||=
    azure_client_for_source.commits

  @recent_azure_commit_messages.
    find { |c| azure_commit_author_email(c) == dependabot_email }&.
    message&.
    strip
end
last_bitbucket_dependabot_commit_message() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 383
def last_bitbucket_dependabot_commit_message
  @recent_bitbucket_commit_messages ||=
    bitbucket_client_for_source.commits(source.repo)

  @recent_bitbucket_commit_messages.
    find { |c| bitbucket_commit_author_email(c) == dependabot_email }&.
    fetch("message", nil)&.
    strip
end
last_codecommit_dependabot_commit_message() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 393
def last_codecommit_dependabot_commit_message
  @recent_codecommit_commit_messages ||=
    codecommit_client_for_source.commits(source.repo)

  @recent_codecommit_commit_messages.commits.
    find { |c| c.author.email == dependabot_email }&.
    message&.
    strip
end
last_dependabot_commit_message() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 335
def last_dependabot_commit_message
  @last_dependabot_commit_message ||=
    case source.provider
    when "github" then last_github_dependabot_commit_message
    when "gitlab" then last_gitlab_dependabot_commit_message
    when "azure" then last_azure_dependabot_commit_message
    when "bitbucket" then last_bitbucket_dependabot_commit_message
    when "codecommit" then last_codecommit_dependabot_commit_message
    else raise "Unsupported provider: #{source.provider}"
    end
end
last_dependabot_commit_prefix() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 162
def last_dependabot_commit_prefix
  last_dependabot_commit_message&.split(/[:(]/)&.first
end
last_dependabot_commit_style() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 152
def last_dependabot_commit_style
  return unless (msg = last_dependabot_commit_message)

  return :gitmoji if msg.start_with?("⬆️")
  return :conventional_prefix if msg.match?(/\A(chore|build|upgrade):/i)
  return unless msg.match?(/\A(chore|build|upgrade)\(/i)

  :conventional_prefix_with_scope
end
last_github_dependabot_commit_message() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 347
def last_github_dependabot_commit_message
  recent_github_commits.
    reject { |c| c.commit&.message&.start_with?("Merge") }.
    find { |c| c.commit.author&.name&.include?("dependabot") }&.
    commit&.
    message&.
    strip
end
last_gitlab_dependabot_commit_message() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 363
def last_gitlab_dependabot_commit_message
  @recent_gitlab_commit_messages ||=
    gitlab_client_for_source.commits(source.repo)

  @recent_gitlab_commit_messages.
    find { |c| c.author_email == dependabot_email }&.
    message&.
    strip
end
package_manager() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 452
def package_manager
  @package_manager ||= dependencies.first.package_manager
end
prefix_for_last_dependabot_commit_style() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 96
def prefix_for_last_dependabot_commit_style
  case last_dependabot_commit_style
  when :gitmoji then "⬆️ "
  when :conventional_prefix then "#{last_dependabot_commit_prefix}: "
  when :conventional_prefix_with_scope
    "#{last_dependabot_commit_prefix}(#{scope}): "
  else raise "Unknown commit style #{last_dependabot_commit_style}"
  end
end
prefix_from_explicitly_provided_details() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 74
def prefix_from_explicitly_provided_details
  prefix = explicitly_provided_prefix_string
  return if prefix.empty?

  prefix += "(#{scope})" if commit_message_options[:include_scope]
  prefix += ":" if prefix.match?(/[A-Za-z0-9\)\]]\Z/)
  prefix += " " unless prefix.end_with?(" ")
  prefix
end
recent_azure_commit_messages() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 300
def recent_azure_commit_messages
  @recent_azure_commit_messages ||=
    azure_client_for_source.commits

  @recent_azure_commit_messages.
    reject { |c| azure_commit_author_email(c) == dependabot_email }.
    reject { |c| c.fetch("comment")&.start_with?("Merge") }.
    map { |c| c.fetch("comment") }.
    compact.
    map(&:strip)
end
recent_bitbucket_commit_messages() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 312
def recent_bitbucket_commit_messages
  @recent_bitbucket_commit_messages ||=
    bitbucket_client_for_source.commits(source.repo)

  @recent_bitbucket_commit_messages.
    reject { |c| bitbucket_commit_author_email(c) == dependabot_email }.
    map { |c| c.fetch("message", nil) }.
    compact.
    reject { |m| m.start_with?("Merge") }.
    map(&:strip)
end
recent_codecommit_commit_messages() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 324
def recent_codecommit_commit_messages
  @recent_codecommit_commit_messages ||=
    codecommit_client_for_source.commits
  @recent_codecommit_commit_messages.commits.
    reject { |c| c.author.email == dependabot_email }.
    reject { |c| c.message&.start_with?("Merge") }.
    map(&:message).
    compact.
    map(&:strip)
end
recent_commit_messages() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 263
def recent_commit_messages
  case source.provider
  when "github" then recent_github_commit_messages
  when "gitlab" then recent_gitlab_commit_messages
  when "azure" then recent_azure_commit_messages
  when "bitbucket" then recent_bitbucket_commit_messages
  when "codecommit" then recent_codecommit_commit_messages
  else raise "Unsupported provider: #{source.provider}"
  end
end
recent_github_commit_messages() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 278
def recent_github_commit_messages
  recent_github_commits.
    reject { |c| c.author&.type == "Bot" }.
    reject { |c| c.commit&.message&.start_with?("Merge") }.
    map(&:commit).
    map(&:message).
    compact.
    map(&:strip)
end
recent_github_commits() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 356
def recent_github_commits
  @recent_github_commits ||=
    github_client_for_source.commits(source.repo, per_page: 100)
rescue Octokit::Conflict, Octokit::NotFound
  @recent_github_commits ||= []
end
recent_gitlab_commit_messages() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 288
def recent_gitlab_commit_messages
  @recent_gitlab_commit_messages ||=
    gitlab_client_for_source.commits(source.repo)

  @recent_gitlab_commit_messages.
    reject { |c| c.author_email == dependabot_email }.
    reject { |c| c.message&.start_with?("merge !") }.
    map(&:message).
    compact.
    map(&:strip)
end
scope() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 125
def scope
  dependencies.any?(&:production?) ? "deps" : "deps-dev"
end
security_fix?() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 57
def security_fix?
  @security_fix
end
security_prefix() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 106
def security_prefix
  return "🔒 " if commit_prefix == "⬆️ "

  capitalize_first_word? ? "[Security] " : "[security] "
end
using_angular_commit_messages?() click to toggle source

rubocop:disable Metrics/PerceivedComplexity

# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 167
def using_angular_commit_messages?
  return false if recent_commit_messages.none?

  angular_messages = recent_commit_messages.select do |message|
    ANGULAR_PREFIXES.any? { |pre| message.match?(/#{pre}[:(]/i) }
  end

  # Definitely not using Angular commits if < 30% match angular commits
  return false if angular_messages.count.to_f / recent_commit_messages.count < 0.3

  eslint_only_pres = ESLINT_PREFIXES.map(&:downcase) - ANGULAR_PREFIXES
  angular_only_pres = ANGULAR_PREFIXES - ESLINT_PREFIXES.map(&:downcase)

  uses_eslint_only_pres =
    recent_commit_messages.
    any? { |m| eslint_only_pres.any? { |pre| m.match?(/#{pre}[:(]/i) } }

  uses_angular_only_pres =
    recent_commit_messages.
    any? { |m| angular_only_pres.any? { |pre| m.match?(/#{pre}[:(]/i) } }

  # If using any angular-only prefixes, return true
  # (i.e., we assume Angular over ESLint when both are present)
  return true if uses_angular_only_pres
  return false if uses_eslint_only_pres

  true
end
using_eslint_commit_messages?() click to toggle source

rubocop:enable Metrics/PerceivedComplexity

# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 197
def using_eslint_commit_messages?
  return false if recent_commit_messages.none?

  semantic_messages = recent_commit_messages.select do |message|
    ESLINT_PREFIXES.any? { |pre| message.start_with?(/#{pre}[:(]/) }
  end

  semantic_messages.count.to_f / recent_commit_messages.count > 0.3
end
using_gitmoji_commit_messages?() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 253
def using_gitmoji_commit_messages?
  return false unless recent_commit_messages.any?

  gitmoji_messages =
    recent_commit_messages.
    select { |m| GITMOJI_PREFIXES.any? { |pre| m.match?(/:#{pre}:/i) } }

  gitmoji_messages.count / recent_commit_messages.count.to_f > 0.3
end
using_prefixed_commit_messages?() click to toggle source
# File lib/dependabot/pull_request_creator/pr_name_prefixer.rb, line 207
def using_prefixed_commit_messages?
  return false if using_gitmoji_commit_messages?
  return false if recent_commit_messages.none?

  prefixed_messages = recent_commit_messages.select do |message|
    message.start_with?(/[a-z][^\s]+:/)
  end

  prefixed_messages.count.to_f / recent_commit_messages.count > 0.3
end