Class: Dependabot::PullRequestCreator::BranchNameTemplate

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/dependabot/pull_request_creator/branch_name_template.rb

Defined Under Namespace

Classes: Error

Constant Summary collapse

SOLO_PLACEHOLDERS =
T.let(
  %w(prefix package_manager directory target_branch dependency version name).freeze,
  T::Array[String]
)
GROUP_PLACEHOLDERS =
T.let(
  %w(prefix package_manager directory target_branch group_name name).freeze,
  T::Array[String]
)
MULTI_ECO_PLACEHOLDERS =
T.let(
  %w(prefix target_branch group_name name).freeze,
  T::Array[String]
)

Class Method Summary collapse

Class Method Details

.allowed_placeholders(strategy) ⇒ Object



47
48
49
50
51
52
53
54
55
# File 'lib/dependabot/pull_request_creator/branch_name_template.rb', line 47

def self.allowed_placeholders(strategy)
  case strategy
  when :solo then SOLO_PLACEHOLDERS
  when :group then GROUP_PLACEHOLDERS
  when :multi_ecosystem then MULTI_ECO_PLACEHOLDERS
  else
    raise Error, "Unknown strategy: #{strategy}"
  end
end

.collect_template_errors(template, allowed, strategy) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/dependabot/pull_request_creator/branch_name_template.rb', line 58

def self.collect_template_errors(template, allowed, strategy)
  used = template.scan(/\{(\w+)\}/).flatten
  unknown = used.uniq.reject { |t| allowed.include?(t) }
  unknown -= ["package_manager"] if strategy == :multi_ecosystem && used.include?("package_manager")
  malformed = template.gsub(/\{\w+\}/, "").match?(/[{}]/)

  messages = T.let([], T::Array[String])
  messages << "Unknown placeholder(s): #{unknown.map { |v| "{#{v}}" }.join(', ')}" unless unknown.empty?
  messages << "Malformed or unclosed braces detected." if malformed

  if strategy == :multi_ecosystem && used.include?("package_manager")
    messages << "{package_manager} is not available for multi-ecosystem groups (spans multiple ecosystems)."
  end

  messages
end

.render(template, vars, strategy:, digest: nil) ⇒ Object

Raises:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/dependabot/pull_request_creator/branch_name_template.rb', line 113

def self.render(template, vars, strategy:, digest: nil)
  validate_template(template, strategy: strategy)

  # Substitute placeholders
  name = template.gsub(/\{(\w+)\}/) do
    key = T.must(Regexp.last_match(1))
    raise Error, "Missing value for placeholder \"{#{key}}\"." unless vars.key?(key)

    T.must(vars[key])
  end

  # Auto-append digest for Group/MultiEcosystem
  name = "#{name}-#{digest}" if digest && strategy != :solo

  # Collapse empty path segments (e.g. {target_branch} when nil) and strip edge slashes
  name = name.squeeze("/").gsub(%r{\A/|/\z}, "")
  raise Error, "Resolved branch name is empty." if name.empty?

  # Validate git ref after all transformations
  validate_ref_name(name)

  name
end

.validate_ref_name(name) ⇒ Object

rubocop:disable Naming/PredicateMethod

Raises:



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/dependabot/pull_request_creator/branch_name_template.rb', line 80

def self.validate_ref_name(name) # rubocop:disable Naming/PredicateMethod
  illegal = %r{
    [\x00-\x1F\x7F\ ~^:?*\[\\] |
    \.\.                         |
    @\{                          |
    //                           |
    /\.                          |
    \A/                          |
    /\z                          |
    \.lock\z                     |
    \A-                          |
    \.\z                         |
    \A@\z
  }x

  raise Error, "Resolved branch name \"#{name}\" is not a valid Git ref." if name.match?(illegal)

  true
end

.validate_template(template, strategy:) ⇒ Object

rubocop:disable Naming/PredicateMethod

Raises:



35
36
37
38
39
40
41
42
43
44
# File 'lib/dependabot/pull_request_creator/branch_name_template.rb', line 35

def self.validate_template(template, strategy:) # rubocop:disable Naming/PredicateMethod
  raise Error, "Template must be a non-empty string." if template.empty?

  allowed = allowed_placeholders(strategy)
  messages = collect_template_errors(template, allowed, strategy)

  raise Error, "#{messages.join("\n")}\nAllowed: #{allowed.map { |v| "{#{v}}" }.join(', ')}" if messages.any?

  true
end