dependabot-common 0.385.0 → 0.386.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dependabot/dependency_graphers/base.rb +103 -0
- data/lib/dependabot/pull_request_creator/branch_name_template.rb +138 -0
- data/lib/dependabot/pull_request_creator/branch_namer/base.rb +62 -2
- data/lib/dependabot/pull_request_creator/branch_namer/dependency_group_strategy.rb +37 -2
- data/lib/dependabot/pull_request_creator/branch_namer/multi_ecosystem_strategy.rb +32 -2
- data/lib/dependabot/pull_request_creator/branch_namer/solo_strategy.rb +42 -16
- data/lib/dependabot/pull_request_creator/branch_namer.rb +30 -2
- data/lib/dependabot/pull_request_creator.rb +22 -1
- data/lib/dependabot/source.rb +25 -5
- data/lib/dependabot.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e4c26ec485eed8ebb4e35d9edfa4ab996658f450708439941ff8ca253ce0457
|
|
4
|
+
data.tar.gz: '045951912dda5a814efcf305b11ae28e5c6cd97a1e7f72f1c610650d2f9031cb'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb49cbf946335c722ca445b0c314909eb0cf1790b241b68461881873cf254afddb7b7b10a168879ded348af64bdb30f5093882c1221b8f3c7e3ca69bbf6bd59b
|
|
7
|
+
data.tar.gz: ed0b8a8a019adeed81707ee2aae27e1e7e50d8f4c0da2d66cfca4a952df713173706d091062d02cb5a5aa4c18b819747b76ec5f114595fdbb804c4bef9df208b
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
5
5
|
|
|
6
|
+
require "dependabot/dependency_file"
|
|
7
|
+
|
|
6
8
|
module Dependabot
|
|
7
9
|
module DependencyGraphers
|
|
8
10
|
# This is a small value class that specifies the information we expect to be returned for each
|
|
@@ -20,6 +22,29 @@ module Dependabot
|
|
|
20
22
|
const :dependencies, T::Array[String]
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
# A manifest group is a subset of a directory's dependency files that stands on its own as a valid
|
|
26
|
+
# parser input, plus the single file that the group's dependencies should be attributed to.
|
|
27
|
+
#
|
|
28
|
+
# - `primary` is the attribution target (the file that owns the group's dependencies in the snapshot).
|
|
29
|
+
# - `files` is everything the parser needs to resolve the group, including the primary and any sibling
|
|
30
|
+
# files pulled in only to satisfy cross-references.
|
|
31
|
+
#
|
|
32
|
+
# Most ecosystems have exactly one group per directory (a manifest + optional lockfile).
|
|
33
|
+
#
|
|
34
|
+
# Ecosystems where multiple independent manifests routinely share a directory override `manifest_groups`
|
|
35
|
+
# to return one group per independent manifest using ecosystem-specific rules (e.g. Python layered requirements)
|
|
36
|
+
class ManifestGroup < T::ImmutableStruct
|
|
37
|
+
const :primary, Dependabot::DependencyFile
|
|
38
|
+
const :files, T::Array[Dependabot::DependencyFile]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The resolved output for a single manifest group: the file to attribute to, and the dependencies
|
|
42
|
+
# resolved from that group's files.
|
|
43
|
+
class ManifestGroupSnapshot < T::ImmutableStruct
|
|
44
|
+
const :manifest_file, Dependabot::DependencyFile
|
|
45
|
+
const :resolved_dependencies, T::Hash[String, ResolvedDependency]
|
|
46
|
+
end
|
|
47
|
+
|
|
23
48
|
class Base
|
|
24
49
|
extend T::Sig
|
|
25
50
|
extend T::Helpers
|
|
@@ -78,8 +103,86 @@ module Dependabot
|
|
|
78
103
|
end
|
|
79
104
|
end
|
|
80
105
|
|
|
106
|
+
# Partitions the parsed directory into one or more manifest groups.
|
|
107
|
+
#
|
|
108
|
+
# The default is a single group spanning the whole directory, attributed to `relevant_dependency_file`.
|
|
109
|
+
#
|
|
110
|
+
# Ecosystems where multiple independent manifests share a directory should override this to return
|
|
111
|
+
# one group per manifest.
|
|
112
|
+
sig { overridable.returns(T::Array[ManifestGroup]) }
|
|
113
|
+
def manifest_groups
|
|
114
|
+
[ManifestGroup.new(primary: relevant_dependency_file, files: dependency_files)]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Resolves each manifest group into a snapshot.
|
|
118
|
+
#
|
|
119
|
+
# When there is a single group, the common case for most ecosystems, we attribute all resolved dependencies
|
|
120
|
+
# to the group's primary without further work required.
|
|
121
|
+
#
|
|
122
|
+
# When there are multiple groups, we instantiate a new scoped grapher for the group's files and resolve each
|
|
123
|
+
# independently.
|
|
124
|
+
sig { returns(T::Array[ManifestGroupSnapshot]) }
|
|
125
|
+
def manifest_group_snapshots
|
|
126
|
+
@manifest_group_snapshots ||= T.let(
|
|
127
|
+
build_manifest_group_snapshots,
|
|
128
|
+
T.nilable(T::Array[ManifestGroupSnapshot])
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
|
|
81
132
|
private
|
|
82
133
|
|
|
134
|
+
sig { returns(T::Array[ManifestGroupSnapshot]) }
|
|
135
|
+
def build_manifest_group_snapshots
|
|
136
|
+
groups = manifest_groups
|
|
137
|
+
|
|
138
|
+
if groups.one?
|
|
139
|
+
group = T.must(groups.first)
|
|
140
|
+
return [ManifestGroupSnapshot.new(
|
|
141
|
+
manifest_file: group.primary,
|
|
142
|
+
resolved_dependencies: resolved_dependencies
|
|
143
|
+
)]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
groups.map do |group|
|
|
147
|
+
scoped = scoped_grapher(group.files)
|
|
148
|
+
snapshot = ManifestGroupSnapshot.new(
|
|
149
|
+
manifest_file: group.primary,
|
|
150
|
+
resolved_dependencies: scoped.resolved_dependencies
|
|
151
|
+
)
|
|
152
|
+
# Ensure we propagate any error flags from the scoped grapher.
|
|
153
|
+
absorb_error_state(scoped)
|
|
154
|
+
snapshot
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Propagates a scoped grapher's subdependency error state onto this grapher so callers can inspect the
|
|
159
|
+
# aggregate result of resolving every group.
|
|
160
|
+
sig { params(scoped: Dependabot::DependencyGraphers::Base).void }
|
|
161
|
+
def absorb_error_state(scoped)
|
|
162
|
+
return unless scoped.errored_fetching_subdependencies
|
|
163
|
+
|
|
164
|
+
errored_fetching_subdependencies!
|
|
165
|
+
# We keep the last subdependency error we see - the full error dialogue will be present in the logs,
|
|
166
|
+
# this just ensures job summary dialogues have something to show.
|
|
167
|
+
@subdependency_error = scoped.subdependency_error unless scoped.subdependency_error.nil?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Builds a grapher of the same class scoped to a subset of the directory's files, reusing the current
|
|
171
|
+
# file parser's configuration. Used to resolve a single manifest group in isolation.
|
|
172
|
+
sig { params(files: T::Array[Dependabot::DependencyFile]).returns(Dependabot::DependencyGraphers::Base) }
|
|
173
|
+
def scoped_grapher(files)
|
|
174
|
+
scoped_parser = file_parser.class.new(
|
|
175
|
+
dependency_files: files,
|
|
176
|
+
source: file_parser.source,
|
|
177
|
+
repo_contents_path: file_parser.repo_contents_path,
|
|
178
|
+
credentials: file_parser.credentials,
|
|
179
|
+
reject_external_code: file_parser.reject_external_code?,
|
|
180
|
+
options: file_parser.options
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
self.class.new(file_parser: scoped_parser)
|
|
184
|
+
end
|
|
185
|
+
|
|
83
186
|
sig { returns(Dependabot::FileParsers::Base) }
|
|
84
187
|
attr_reader :file_parser
|
|
85
188
|
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
require "dependabot/errors"
|
|
7
|
+
require "dependabot/pull_request_creator"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
class PullRequestCreator
|
|
11
|
+
class BranchNameTemplate
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
class Error < Dependabot::DependabotError; end
|
|
15
|
+
|
|
16
|
+
SOLO_PLACEHOLDERS = T.let(
|
|
17
|
+
%w(prefix package_manager directory target_branch dependency version name).freeze,
|
|
18
|
+
T::Array[String]
|
|
19
|
+
)
|
|
20
|
+
GROUP_PLACEHOLDERS = T.let(
|
|
21
|
+
%w(prefix package_manager directory target_branch group_name name).freeze,
|
|
22
|
+
T::Array[String]
|
|
23
|
+
)
|
|
24
|
+
MULTI_ECO_PLACEHOLDERS = T.let(
|
|
25
|
+
%w(prefix target_branch group_name name).freeze,
|
|
26
|
+
T::Array[String]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------
|
|
30
|
+
# 1. validate_template(template, strategy:)
|
|
31
|
+
# Ensures every {token} is allowed for the given strategy,
|
|
32
|
+
# brackets are well-formed, and no forbidden placeholders used.
|
|
33
|
+
# ---------------------------------------------------------------
|
|
34
|
+
sig { params(template: String, strategy: Symbol).returns(T::Boolean) }
|
|
35
|
+
def self.validate_template(template, strategy:) # rubocop:disable Naming/PredicateMethod
|
|
36
|
+
raise Error, "Template must be a non-empty string." if template.empty?
|
|
37
|
+
|
|
38
|
+
allowed = allowed_placeholders(strategy)
|
|
39
|
+
messages = collect_template_errors(template, allowed, strategy)
|
|
40
|
+
|
|
41
|
+
raise Error, "#{messages.join("\n")}\nAllowed: #{allowed.map { |v| "{#{v}}" }.join(', ')}" if messages.any?
|
|
42
|
+
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
sig { params(strategy: Symbol).returns(T::Array[String]) }
|
|
47
|
+
def self.allowed_placeholders(strategy)
|
|
48
|
+
case strategy
|
|
49
|
+
when :solo then SOLO_PLACEHOLDERS
|
|
50
|
+
when :group then GROUP_PLACEHOLDERS
|
|
51
|
+
when :multi_ecosystem then MULTI_ECO_PLACEHOLDERS
|
|
52
|
+
else
|
|
53
|
+
raise Error, "Unknown strategy: #{strategy}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
sig { params(template: String, allowed: T::Array[String], strategy: Symbol).returns(T::Array[String]) }
|
|
58
|
+
def self.collect_template_errors(template, allowed, strategy)
|
|
59
|
+
used = template.scan(/\{(\w+)\}/).flatten
|
|
60
|
+
unknown = used.uniq.reject { |t| allowed.include?(t) }
|
|
61
|
+
unknown -= ["package_manager"] if strategy == :multi_ecosystem && used.include?("package_manager")
|
|
62
|
+
malformed = template.gsub(/\{\w+\}/, "").match?(/[{}]/)
|
|
63
|
+
|
|
64
|
+
messages = T.let([], T::Array[String])
|
|
65
|
+
messages << "Unknown placeholder(s): #{unknown.map { |v| "{#{v}}" }.join(', ')}" unless unknown.empty?
|
|
66
|
+
messages << "Malformed or unclosed braces detected." if malformed
|
|
67
|
+
|
|
68
|
+
if strategy == :multi_ecosystem && used.include?("package_manager")
|
|
69
|
+
messages << "{package_manager} is not available for multi-ecosystem groups (spans multiple ecosystems)."
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
messages
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# ---------------------------------------------------------------
|
|
76
|
+
# 2. validate_ref_name(name)
|
|
77
|
+
# Validates the final branch name against Git ref rules.
|
|
78
|
+
# ---------------------------------------------------------------
|
|
79
|
+
sig { params(name: String).returns(T::Boolean) }
|
|
80
|
+
def self.validate_ref_name(name) # rubocop:disable Naming/PredicateMethod
|
|
81
|
+
illegal = %r{
|
|
82
|
+
[\x00-\x1F\x7F\ ~^:?*\[\\] |
|
|
83
|
+
\.\. |
|
|
84
|
+
@\{ |
|
|
85
|
+
// |
|
|
86
|
+
/\. |
|
|
87
|
+
\A/ |
|
|
88
|
+
/\z |
|
|
89
|
+
\.lock\z |
|
|
90
|
+
\A- |
|
|
91
|
+
\.\z |
|
|
92
|
+
\A@\z
|
|
93
|
+
}x
|
|
94
|
+
|
|
95
|
+
raise Error, "Resolved branch name \"#{name}\" is not a valid Git ref." if name.match?(illegal)
|
|
96
|
+
|
|
97
|
+
true
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# ---------------------------------------------------------------
|
|
101
|
+
# 3. render(template, vars, strategy:, digest: nil)
|
|
102
|
+
# Full pipeline: validate template -> substitute -> append digest
|
|
103
|
+
# -> validate ref.
|
|
104
|
+
# ---------------------------------------------------------------
|
|
105
|
+
sig do
|
|
106
|
+
params(
|
|
107
|
+
template: String,
|
|
108
|
+
vars: T::Hash[String, String],
|
|
109
|
+
strategy: Symbol,
|
|
110
|
+
digest: T.nilable(String)
|
|
111
|
+
).returns(String)
|
|
112
|
+
end
|
|
113
|
+
def self.render(template, vars, strategy:, digest: nil)
|
|
114
|
+
validate_template(template, strategy: strategy)
|
|
115
|
+
|
|
116
|
+
# Substitute placeholders
|
|
117
|
+
name = template.gsub(/\{(\w+)\}/) do
|
|
118
|
+
key = T.must(Regexp.last_match(1))
|
|
119
|
+
raise Error, "Missing value for placeholder \"{#{key}}\"." unless vars.key?(key)
|
|
120
|
+
|
|
121
|
+
T.must(vars[key])
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Auto-append digest for Group/MultiEcosystem
|
|
125
|
+
name = "#{name}-#{digest}" if digest && strategy != :solo
|
|
126
|
+
|
|
127
|
+
# Collapse empty path segments (e.g. {target_branch} when nil) and strip edge slashes
|
|
128
|
+
name = name.squeeze("/").gsub(%r{\A/|/\z}, "")
|
|
129
|
+
raise Error, "Resolved branch name is empty." if name.empty?
|
|
130
|
+
|
|
131
|
+
# Validate git ref after all transformations
|
|
132
|
+
validate_ref_name(name)
|
|
133
|
+
|
|
134
|
+
name
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
require "digest"
|
|
5
5
|
require "sorbet-runtime"
|
|
6
6
|
|
|
7
|
+
require "dependabot/pull_request_creator/branch_name_template"
|
|
8
|
+
|
|
7
9
|
module Dependabot
|
|
8
10
|
class PullRequestCreator
|
|
9
11
|
class BranchNamer
|
|
@@ -28,6 +30,15 @@ module Dependabot
|
|
|
28
30
|
sig { returns(T.nilable(Integer)) }
|
|
29
31
|
attr_reader :max_length
|
|
30
32
|
|
|
33
|
+
sig { returns(T.nilable(String)) }
|
|
34
|
+
attr_reader :word_separator
|
|
35
|
+
|
|
36
|
+
sig { returns(T.nilable(String)) }
|
|
37
|
+
attr_reader :branch_name_case
|
|
38
|
+
|
|
39
|
+
sig { returns(T.nilable(String)) }
|
|
40
|
+
attr_reader :template
|
|
41
|
+
|
|
31
42
|
sig do
|
|
32
43
|
params(
|
|
33
44
|
dependencies: T::Array[Dependency],
|
|
@@ -35,7 +46,10 @@ module Dependabot
|
|
|
35
46
|
target_branch: T.nilable(String),
|
|
36
47
|
separator: String,
|
|
37
48
|
prefix: String,
|
|
38
|
-
max_length: T.nilable(Integer)
|
|
49
|
+
max_length: T.nilable(Integer),
|
|
50
|
+
word_separator: T.nilable(String),
|
|
51
|
+
branch_name_case: T.nilable(String),
|
|
52
|
+
template: T.nilable(String)
|
|
39
53
|
)
|
|
40
54
|
.void
|
|
41
55
|
end
|
|
@@ -45,7 +59,10 @@ module Dependabot
|
|
|
45
59
|
target_branch:,
|
|
46
60
|
separator: "/",
|
|
47
61
|
prefix: "dependabot",
|
|
48
|
-
max_length: nil
|
|
62
|
+
max_length: nil,
|
|
63
|
+
word_separator: nil,
|
|
64
|
+
branch_name_case: nil,
|
|
65
|
+
template: nil
|
|
49
66
|
)
|
|
50
67
|
@dependencies = dependencies
|
|
51
68
|
@files = files
|
|
@@ -53,6 +70,9 @@ module Dependabot
|
|
|
53
70
|
@separator = separator
|
|
54
71
|
@prefix = prefix
|
|
55
72
|
@max_length = max_length
|
|
73
|
+
@word_separator = word_separator
|
|
74
|
+
@branch_name_case = branch_name_case
|
|
75
|
+
@template = template
|
|
56
76
|
end
|
|
57
77
|
|
|
58
78
|
sig { overridable.returns(String) }
|
|
@@ -62,6 +82,25 @@ module Dependabot
|
|
|
62
82
|
|
|
63
83
|
private
|
|
64
84
|
|
|
85
|
+
sig do
|
|
86
|
+
params(
|
|
87
|
+
vars: T::Hash[String, String],
|
|
88
|
+
strategy: Symbol,
|
|
89
|
+
digest: T.nilable(String)
|
|
90
|
+
).returns(String)
|
|
91
|
+
end
|
|
92
|
+
def render_from_template(vars:, strategy:, digest: nil)
|
|
93
|
+
rendered = BranchNameTemplate.render(
|
|
94
|
+
T.must(template),
|
|
95
|
+
vars,
|
|
96
|
+
strategy: strategy,
|
|
97
|
+
digest: digest
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Apply post-processing (separator, word_separator, case) and max-length
|
|
101
|
+
sanitize_branch_name(rendered)
|
|
102
|
+
end
|
|
103
|
+
|
|
65
104
|
sig { params(ref_name: String).returns(String) }
|
|
66
105
|
def sanitize_branch_name(ref_name)
|
|
67
106
|
# General git ref validation
|
|
@@ -70,6 +109,27 @@ module Dependabot
|
|
|
70
109
|
# Some users need branch names without slashes
|
|
71
110
|
sanitized_name = sanitized_name.gsub("/", separator)
|
|
72
111
|
|
|
112
|
+
# Apply word_separator and case transformation only to content after the prefix,
|
|
113
|
+
# preserving the user-configured prefix as-is.
|
|
114
|
+
if word_separator || branch_name_case
|
|
115
|
+
prefix_with_sep = "#{prefix}#{separator}"
|
|
116
|
+
prefix_part = sanitized_name.start_with?(prefix_with_sep) ? prefix_with_sep : ""
|
|
117
|
+
content = sanitized_name.delete_prefix(prefix_with_sep)
|
|
118
|
+
|
|
119
|
+
# Replace underscores with word_separator in the content after prefix
|
|
120
|
+
content = content.gsub("_", T.must(word_separator)) if word_separator
|
|
121
|
+
|
|
122
|
+
# Apply case transformation to content after prefix
|
|
123
|
+
case branch_name_case
|
|
124
|
+
when "lower"
|
|
125
|
+
content = content.downcase
|
|
126
|
+
when "upper"
|
|
127
|
+
content = content.upcase
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
sanitized_name = "#{prefix_part}#{content}"
|
|
131
|
+
end
|
|
132
|
+
|
|
73
133
|
# Shorten the ref in case users refs have length limits
|
|
74
134
|
branch_name_max_length = max_length
|
|
75
135
|
if branch_name_max_length && (sanitized_name.length > branch_name_max_length)
|
|
@@ -19,7 +19,10 @@ module Dependabot
|
|
|
19
19
|
includes_security_fixes: T::Boolean,
|
|
20
20
|
separator: String,
|
|
21
21
|
prefix: String,
|
|
22
|
-
max_length: T.nilable(Integer)
|
|
22
|
+
max_length: T.nilable(Integer),
|
|
23
|
+
word_separator: T.nilable(String),
|
|
24
|
+
branch_name_case: T.nilable(String),
|
|
25
|
+
template: T.nilable(String)
|
|
23
26
|
)
|
|
24
27
|
.void
|
|
25
28
|
end
|
|
@@ -31,7 +34,10 @@ module Dependabot
|
|
|
31
34
|
includes_security_fixes:,
|
|
32
35
|
separator: "/",
|
|
33
36
|
prefix: "dependabot",
|
|
34
|
-
max_length: nil
|
|
37
|
+
max_length: nil,
|
|
38
|
+
word_separator: nil,
|
|
39
|
+
branch_name_case: nil,
|
|
40
|
+
template: nil
|
|
35
41
|
)
|
|
36
42
|
super(
|
|
37
43
|
dependencies: dependencies,
|
|
@@ -40,6 +46,9 @@ module Dependabot
|
|
|
40
46
|
separator: separator,
|
|
41
47
|
prefix: prefix,
|
|
42
48
|
max_length: max_length,
|
|
49
|
+
word_separator: word_separator,
|
|
50
|
+
branch_name_case: branch_name_case,
|
|
51
|
+
template: template,
|
|
43
52
|
)
|
|
44
53
|
|
|
45
54
|
@dependency_group = dependency_group
|
|
@@ -48,6 +57,14 @@ module Dependabot
|
|
|
48
57
|
|
|
49
58
|
sig { override.returns(String) }
|
|
50
59
|
def new_branch_name
|
|
60
|
+
if template
|
|
61
|
+
return render_from_template(
|
|
62
|
+
vars: template_vars,
|
|
63
|
+
strategy: :group,
|
|
64
|
+
digest: dependency_digest
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
51
68
|
sanitize_branch_name(File.join(prefixes, group_name_with_dependency_digest))
|
|
52
69
|
end
|
|
53
70
|
|
|
@@ -56,6 +73,24 @@ module Dependabot
|
|
|
56
73
|
sig { returns(Dependabot::DependencyGroup) }
|
|
57
74
|
attr_reader :dependency_group
|
|
58
75
|
|
|
76
|
+
sig { returns(T::Hash[String, String]) }
|
|
77
|
+
def template_vars
|
|
78
|
+
directory_part = (directory || "/").sub(%r{^/}, "")
|
|
79
|
+
directory_part = "root" if directory_part.empty?
|
|
80
|
+
|
|
81
|
+
group_name = sanitize_ref(dependency_group.name.tr(" ", "-"))
|
|
82
|
+
|
|
83
|
+
vars = {
|
|
84
|
+
"prefix" => prefix,
|
|
85
|
+
"package_manager" => package_manager,
|
|
86
|
+
"directory" => directory_part,
|
|
87
|
+
"group_name" => group_name,
|
|
88
|
+
"name" => group_name,
|
|
89
|
+
"target_branch" => target_branch || ""
|
|
90
|
+
}
|
|
91
|
+
vars
|
|
92
|
+
end
|
|
93
|
+
|
|
59
94
|
sig { returns(T::Array[String]) }
|
|
60
95
|
def prefixes
|
|
61
96
|
[
|
|
@@ -19,7 +19,10 @@ module Dependabot
|
|
|
19
19
|
multi_ecosystem_name: String,
|
|
20
20
|
separator: String,
|
|
21
21
|
prefix: String,
|
|
22
|
-
max_length: T.nilable(Integer)
|
|
22
|
+
max_length: T.nilable(Integer),
|
|
23
|
+
word_separator: T.nilable(String),
|
|
24
|
+
branch_name_case: T.nilable(String),
|
|
25
|
+
template: T.nilable(String)
|
|
23
26
|
)
|
|
24
27
|
.void
|
|
25
28
|
end
|
|
@@ -31,7 +34,10 @@ module Dependabot
|
|
|
31
34
|
multi_ecosystem_name:,
|
|
32
35
|
separator: "/",
|
|
33
36
|
prefix: "dependabot",
|
|
34
|
-
max_length: nil
|
|
37
|
+
max_length: nil,
|
|
38
|
+
word_separator: nil,
|
|
39
|
+
branch_name_case: nil,
|
|
40
|
+
template: nil
|
|
35
41
|
)
|
|
36
42
|
super(
|
|
37
43
|
dependencies: dependencies,
|
|
@@ -40,6 +46,9 @@ module Dependabot
|
|
|
40
46
|
separator: separator,
|
|
41
47
|
prefix: prefix,
|
|
42
48
|
max_length: max_length,
|
|
49
|
+
word_separator: word_separator,
|
|
50
|
+
branch_name_case: branch_name_case,
|
|
51
|
+
template: template,
|
|
43
52
|
)
|
|
44
53
|
|
|
45
54
|
@multi_ecosystem_name = multi_ecosystem_name
|
|
@@ -48,6 +57,14 @@ module Dependabot
|
|
|
48
57
|
|
|
49
58
|
sig { override.returns(String) }
|
|
50
59
|
def new_branch_name
|
|
60
|
+
if template
|
|
61
|
+
return render_from_template(
|
|
62
|
+
vars: template_vars,
|
|
63
|
+
strategy: :multi_ecosystem,
|
|
64
|
+
digest: dependency_digest
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
51
68
|
sanitize_branch_name(File.join(prefixes, group_name_with_dependency_digest))
|
|
52
69
|
end
|
|
53
70
|
|
|
@@ -56,6 +73,19 @@ module Dependabot
|
|
|
56
73
|
sig { returns(String) }
|
|
57
74
|
attr_reader :multi_ecosystem_name
|
|
58
75
|
|
|
76
|
+
sig { returns(T::Hash[String, String]) }
|
|
77
|
+
def template_vars
|
|
78
|
+
multi_name = sanitize_ref(multi_ecosystem_name.tr(" ", "-"))
|
|
79
|
+
|
|
80
|
+
vars = {
|
|
81
|
+
"prefix" => prefix,
|
|
82
|
+
"group_name" => multi_name,
|
|
83
|
+
"name" => multi_name,
|
|
84
|
+
"target_branch" => target_branch || ""
|
|
85
|
+
}
|
|
86
|
+
vars
|
|
87
|
+
end
|
|
88
|
+
|
|
59
89
|
sig { returns(T::Array[String]) }
|
|
60
90
|
def prefixes
|
|
61
91
|
[
|
|
@@ -15,26 +15,18 @@ module Dependabot
|
|
|
15
15
|
|
|
16
16
|
sig { override.returns(String) }
|
|
17
17
|
def new_branch_name
|
|
18
|
+
if template
|
|
19
|
+
return render_from_template(
|
|
20
|
+
vars: template_vars,
|
|
21
|
+
strategy: :solo
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
18
25
|
return short_branch_name if branch_name_might_be_long?
|
|
19
26
|
|
|
20
27
|
@name ||=
|
|
21
28
|
T.let(
|
|
22
|
-
|
|
23
|
-
dependency_name_part =
|
|
24
|
-
if dependencies.count > 1 && updating_a_property?
|
|
25
|
-
property_name
|
|
26
|
-
elsif dependencies.count > 1 && updating_a_dependency_set?
|
|
27
|
-
dependency_set.fetch(:group)
|
|
28
|
-
else
|
|
29
|
-
dependencies
|
|
30
|
-
.map(&:name)
|
|
31
|
-
.join("-and-")
|
|
32
|
-
.tr(":[]", "-")
|
|
33
|
-
.tr("@", "")
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
"#{dependency_name_part}-#{branch_version_suffix}"
|
|
37
|
-
end,
|
|
29
|
+
"#{template_dependency_name}-#{branch_version_suffix}",
|
|
38
30
|
T.nilable(String)
|
|
39
31
|
)
|
|
40
32
|
|
|
@@ -53,6 +45,40 @@ module Dependabot
|
|
|
53
45
|
].compact
|
|
54
46
|
end
|
|
55
47
|
|
|
48
|
+
sig { returns(T::Hash[String, String]) }
|
|
49
|
+
def template_vars
|
|
50
|
+
dep_name = template_dependency_name
|
|
51
|
+
version = branch_version_suffix || ""
|
|
52
|
+
|
|
53
|
+
vars = {
|
|
54
|
+
"prefix" => prefix,
|
|
55
|
+
"package_manager" => package_manager,
|
|
56
|
+
"directory" => sanitized_directory,
|
|
57
|
+
"dependency" => dep_name,
|
|
58
|
+
"version" => version,
|
|
59
|
+
"name" => "#{dep_name}-#{version}",
|
|
60
|
+
"target_branch" => target_branch || ""
|
|
61
|
+
}
|
|
62
|
+
vars
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
sig { returns(String) }
|
|
66
|
+
def template_dependency_name
|
|
67
|
+
if dependencies.count > 1 && updating_a_property?
|
|
68
|
+
property_name
|
|
69
|
+
elsif dependencies.count > 1 && updating_a_dependency_set?
|
|
70
|
+
dependency_set.fetch(:group)
|
|
71
|
+
else
|
|
72
|
+
dependencies.map(&:name).join("-and-").tr(":[]", "-").tr("@", "")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
sig { returns(String) }
|
|
77
|
+
def sanitized_directory
|
|
78
|
+
dir = (files.first&.directory&.tr(" ", "-") || "/").sub(%r{^/}, "")
|
|
79
|
+
dir.empty? ? "root" : dir
|
|
80
|
+
end
|
|
81
|
+
|
|
56
82
|
sig { returns(String) }
|
|
57
83
|
def package_manager
|
|
58
84
|
T.must(dependencies.first).package_manager
|
|
@@ -9,6 +9,7 @@ require "dependabot/pull_request_creator"
|
|
|
9
9
|
require "dependabot/pull_request_creator/branch_namer/solo_strategy"
|
|
10
10
|
require "dependabot/pull_request_creator/branch_namer/dependency_group_strategy"
|
|
11
11
|
require "dependabot/pull_request_creator/branch_namer/multi_ecosystem_strategy"
|
|
12
|
+
require "dependabot/pull_request_creator/branch_name_template"
|
|
12
13
|
|
|
13
14
|
module Dependabot
|
|
14
15
|
class PullRequestCreator
|
|
@@ -33,6 +34,15 @@ module Dependabot
|
|
|
33
34
|
sig { returns(T.nilable(Integer)) }
|
|
34
35
|
attr_reader :max_length
|
|
35
36
|
|
|
37
|
+
sig { returns(T.nilable(String)) }
|
|
38
|
+
attr_reader :word_separator
|
|
39
|
+
|
|
40
|
+
sig { returns(T.nilable(String)) }
|
|
41
|
+
attr_reader :branch_name_case
|
|
42
|
+
|
|
43
|
+
sig { returns(T.nilable(String)) }
|
|
44
|
+
attr_reader :template
|
|
45
|
+
|
|
36
46
|
sig { returns(T.nilable(Dependabot::DependencyGroup)) }
|
|
37
47
|
attr_reader :dependency_group
|
|
38
48
|
|
|
@@ -51,6 +61,9 @@ module Dependabot
|
|
|
51
61
|
separator: String,
|
|
52
62
|
prefix: String,
|
|
53
63
|
max_length: T.nilable(Integer),
|
|
64
|
+
word_separator: T.nilable(String),
|
|
65
|
+
branch_name_case: T.nilable(String),
|
|
66
|
+
template: T.nilable(String),
|
|
54
67
|
includes_security_fixes: T::Boolean,
|
|
55
68
|
multi_ecosystem_name: T.nilable(String)
|
|
56
69
|
)
|
|
@@ -64,6 +77,9 @@ module Dependabot
|
|
|
64
77
|
separator: "/",
|
|
65
78
|
prefix: "dependabot",
|
|
66
79
|
max_length: nil,
|
|
80
|
+
word_separator: nil,
|
|
81
|
+
branch_name_case: nil,
|
|
82
|
+
template: nil,
|
|
67
83
|
includes_security_fixes: false,
|
|
68
84
|
multi_ecosystem_name: nil
|
|
69
85
|
)
|
|
@@ -74,6 +90,9 @@ module Dependabot
|
|
|
74
90
|
@separator = separator
|
|
75
91
|
@prefix = prefix
|
|
76
92
|
@max_length = max_length
|
|
93
|
+
@word_separator = word_separator
|
|
94
|
+
@branch_name_case = branch_name_case
|
|
95
|
+
@template = template
|
|
77
96
|
@includes_security_fixes = includes_security_fixes
|
|
78
97
|
@multi_ecosystem_name = multi_ecosystem_name
|
|
79
98
|
end
|
|
@@ -109,6 +128,9 @@ module Dependabot
|
|
|
109
128
|
separator: separator,
|
|
110
129
|
prefix: prefix,
|
|
111
130
|
max_length: max_length,
|
|
131
|
+
word_separator: word_separator,
|
|
132
|
+
branch_name_case: branch_name_case,
|
|
133
|
+
template: template,
|
|
112
134
|
multi_ecosystem_name: T.must(multi_ecosystem_name)
|
|
113
135
|
)
|
|
114
136
|
end
|
|
@@ -121,7 +143,10 @@ module Dependabot
|
|
|
121
143
|
target_branch: target_branch,
|
|
122
144
|
separator: separator,
|
|
123
145
|
prefix: prefix,
|
|
124
|
-
max_length: max_length
|
|
146
|
+
max_length: max_length,
|
|
147
|
+
word_separator: word_separator,
|
|
148
|
+
branch_name_case: branch_name_case,
|
|
149
|
+
template: template
|
|
125
150
|
)
|
|
126
151
|
end
|
|
127
152
|
|
|
@@ -135,7 +160,10 @@ module Dependabot
|
|
|
135
160
|
includes_security_fixes: includes_security_fixes,
|
|
136
161
|
separator: separator,
|
|
137
162
|
prefix: prefix,
|
|
138
|
-
max_length: max_length
|
|
163
|
+
max_length: max_length,
|
|
164
|
+
word_separator: word_separator,
|
|
165
|
+
branch_name_case: branch_name_case,
|
|
166
|
+
template: template
|
|
139
167
|
)
|
|
140
168
|
end
|
|
141
169
|
end
|
|
@@ -126,6 +126,15 @@ module Dependabot
|
|
|
126
126
|
sig { returns(T.nilable(Integer)) }
|
|
127
127
|
attr_reader :branch_name_max_length
|
|
128
128
|
|
|
129
|
+
sig { returns(T.nilable(String)) }
|
|
130
|
+
attr_reader :branch_name_word_separator
|
|
131
|
+
|
|
132
|
+
sig { returns(T.nilable(String)) }
|
|
133
|
+
attr_reader :branch_name_case
|
|
134
|
+
|
|
135
|
+
sig { returns(T.nilable(String)) }
|
|
136
|
+
attr_reader :branch_name_template
|
|
137
|
+
|
|
129
138
|
sig { returns(String) }
|
|
130
139
|
attr_reader :github_redirection_service
|
|
131
140
|
|
|
@@ -144,7 +153,7 @@ module Dependabot
|
|
|
144
153
|
sig { returns(T.nilable(Encoding)) }
|
|
145
154
|
attr_reader :pr_message_encoding
|
|
146
155
|
|
|
147
|
-
sig do
|
|
156
|
+
sig do # rubocop:disable Metrics/BlockLength
|
|
148
157
|
params(
|
|
149
158
|
source: Dependabot::Source,
|
|
150
159
|
base_commit: String,
|
|
@@ -164,6 +173,9 @@ module Dependabot
|
|
|
164
173
|
branch_name_separator: String,
|
|
165
174
|
branch_name_prefix: String,
|
|
166
175
|
branch_name_max_length: T.nilable(Integer),
|
|
176
|
+
branch_name_word_separator: T.nilable(String),
|
|
177
|
+
branch_name_case: T.nilable(String),
|
|
178
|
+
branch_name_template: T.nilable(String),
|
|
167
179
|
label_language: T::Boolean,
|
|
168
180
|
automerge_candidate: T::Boolean,
|
|
169
181
|
github_redirection_service: String,
|
|
@@ -198,6 +210,9 @@ module Dependabot
|
|
|
198
210
|
branch_name_separator: "/",
|
|
199
211
|
branch_name_prefix: "dependabot",
|
|
200
212
|
branch_name_max_length: 100,
|
|
213
|
+
branch_name_word_separator: nil,
|
|
214
|
+
branch_name_case: nil,
|
|
215
|
+
branch_name_template: nil,
|
|
201
216
|
label_language: false,
|
|
202
217
|
automerge_candidate: false,
|
|
203
218
|
github_redirection_service: DEFAULT_GITHUB_REDIRECTION_SERVICE,
|
|
@@ -227,6 +242,9 @@ module Dependabot
|
|
|
227
242
|
@branch_name_separator = branch_name_separator
|
|
228
243
|
@branch_name_prefix = branch_name_prefix
|
|
229
244
|
@branch_name_max_length = branch_name_max_length
|
|
245
|
+
@branch_name_word_separator = branch_name_word_separator
|
|
246
|
+
@branch_name_case = branch_name_case
|
|
247
|
+
@branch_name_template = branch_name_template
|
|
230
248
|
@label_language = label_language
|
|
231
249
|
@automerge_candidate = automerge_candidate
|
|
232
250
|
@github_redirection_service = github_redirection_service
|
|
@@ -420,6 +438,9 @@ module Dependabot
|
|
|
420
438
|
separator: branch_name_separator,
|
|
421
439
|
prefix: branch_name_prefix,
|
|
422
440
|
max_length: branch_name_max_length,
|
|
441
|
+
word_separator: branch_name_word_separator,
|
|
442
|
+
branch_name_case: branch_name_case,
|
|
443
|
+
template: branch_name_template,
|
|
423
444
|
includes_security_fixes: includes_security_fixes?
|
|
424
445
|
),
|
|
425
446
|
T.nilable(Dependabot::PullRequestCreator::BranchNamer)
|
data/lib/dependabot/source.rb
CHANGED
|
@@ -126,16 +126,36 @@ module Dependabot
|
|
|
126
126
|
)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
+
@github_enterprise_cache = T.let({}, T::Hash[String, T::Boolean])
|
|
130
|
+
|
|
131
|
+
sig { void }
|
|
132
|
+
def self.reset_github_enterprise_cache!
|
|
133
|
+
@github_enterprise_cache.clear
|
|
134
|
+
end
|
|
135
|
+
|
|
129
136
|
sig { params(base_url: String).returns(T::Boolean) }
|
|
130
137
|
def self.github_enterprise?(base_url)
|
|
138
|
+
return T.must(@github_enterprise_cache[base_url]) if @github_enterprise_cache.key?(base_url)
|
|
139
|
+
|
|
140
|
+
result = detect_github_enterprise(base_url)
|
|
141
|
+
@github_enterprise_cache[base_url] = result unless result.nil?
|
|
142
|
+
result || false
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
sig { params(base_url: String).returns(T.nilable(T::Boolean)) }
|
|
146
|
+
def self.detect_github_enterprise(base_url)
|
|
131
147
|
resp = Excon.get(File.join(base_url, "status"))
|
|
132
|
-
resp.status ==
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
return false if resp.status == 404
|
|
149
|
+
|
|
150
|
+
return unless resp.status == 200
|
|
151
|
+
|
|
152
|
+
# Alternatively: resp.headers["Server"] == "GitHub.com", but this
|
|
153
|
+
# currently doesn't work with development environments
|
|
154
|
+
(resp.headers["X-GitHub-Request-Id"] && !resp.headers["X-GitHub-Request-Id"]&.empty?) || false
|
|
136
155
|
rescue StandardError
|
|
137
|
-
|
|
156
|
+
nil
|
|
138
157
|
end
|
|
158
|
+
private_class_method :detect_github_enterprise
|
|
139
159
|
|
|
140
160
|
sig do
|
|
141
161
|
params(
|
data/lib/dependabot.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-common
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.386.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
@@ -574,6 +574,7 @@ files:
|
|
|
574
574
|
- lib/dependabot/pull_request_creator.rb
|
|
575
575
|
- lib/dependabot/pull_request_creator/azure.rb
|
|
576
576
|
- lib/dependabot/pull_request_creator/bitbucket.rb
|
|
577
|
+
- lib/dependabot/pull_request_creator/branch_name_template.rb
|
|
577
578
|
- lib/dependabot/pull_request_creator/branch_namer.rb
|
|
578
579
|
- lib/dependabot/pull_request_creator/branch_namer/base.rb
|
|
579
580
|
- lib/dependabot/pull_request_creator/branch_namer/dependency_group_strategy.rb
|
|
@@ -621,7 +622,7 @@ licenses:
|
|
|
621
622
|
- MIT
|
|
622
623
|
metadata:
|
|
623
624
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
624
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
625
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.386.0
|
|
625
626
|
rdoc_options: []
|
|
626
627
|
require_paths:
|
|
627
628
|
- lib
|