dependabot-gradle 0.394.0 → 0.396.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/gradle/file_fetcher.rb +1 -6
- data/lib/dependabot/gradle/file_parser.rb +178 -4
- data/lib/dependabot/gradle/file_updater.rb +54 -11
- metadata +9 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '091e3f48c1ef779b93c429cdbcbae77eb751854ebdea7b2f354283d14e76b0ea'
|
|
4
|
+
data.tar.gz: bd6a1439e622474c62d724cac0161f953c41451b8509b0b84abde59aeb8c3cf6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3dea639bb879078a4f3f373e6e8cf30cdc2abe5b993e66eb40c24e8d27ce3f788e4929e7b8c3fd0635c677c86fa6d8b863eee1b998b2eceee120fb7b7ddf9be3
|
|
7
|
+
data.tar.gz: 4a188fc94783c49e1900f67d4390309876d6080ad41b5ab5ef4f527b7f730291d285c6d34e23ec23880f7a23b0c7b4709326a572eddda702bd33847e9ab74b47
|
|
@@ -91,17 +91,12 @@ module Dependabot
|
|
|
91
91
|
files += subproject_buildfiles(root_dir)
|
|
92
92
|
files += subproject_lockfiles(root_dir)
|
|
93
93
|
files += dependency_script_plugins(root_dir)
|
|
94
|
-
files += convention_plugin_source_files(files, root_dir)
|
|
94
|
+
files += convention_plugin_source_files(files, root_dir)
|
|
95
95
|
|
|
96
96
|
files + included_builds(root_dir)
|
|
97
97
|
.flat_map { |dir| all_buildfiles_in_build(dir) }
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
-
sig { returns(T::Boolean) }
|
|
101
|
-
def fetch_plugin_sources_for_lockfiles?
|
|
102
|
-
Dependabot::Experiments.enabled?(:gradle_lockfile_updater)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
100
|
# Only fetch source trees for *nested* project directories (e.g. "build-logic/convention"
|
|
106
101
|
# or "included/convention"), plus the root of a non-top-level build (e.g. "build-logic" or
|
|
107
102
|
# "buildSrc" itself), never the top-level repo root ("app" style modules of the outermost
|
|
@@ -44,6 +44,7 @@ module Dependabot
|
|
|
44
44
|
DEPENDENCY_SET_ENTRY_REGEX = /entry\s+['"](?<name>#{PART})['"]/o
|
|
45
45
|
PLUGIN_BLOCK_DECLARATION_REGEX = /(?:^|\s)plugins\s*\{/
|
|
46
46
|
PLUGIN_ID_REGEX = /['"](?<id>#{PART})['"]/o
|
|
47
|
+
DEPENDENCY_SUBSTITUTION_DECLARATION_REGEX = /\bdependencySubstitution\s*\{/
|
|
47
48
|
|
|
48
49
|
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
|
49
50
|
def parse
|
|
@@ -85,6 +86,153 @@ module Dependabot
|
|
|
85
86
|
.filter_map { |f| dependency_files.find { |bf| bf.name == f } }
|
|
86
87
|
end
|
|
87
88
|
|
|
89
|
+
# Replaces the contents of string literals and comments with spaces,
|
|
90
|
+
# preserving the overall length and newline positions. This lets callers
|
|
91
|
+
# locate structural `{`/`}` braces without being confused by braces that
|
|
92
|
+
# appear inside quoted strings (e.g. `because("see issue {")`), Groovy
|
|
93
|
+
# slashy/dollar-slashy strings (`/.../`, `$/.../$`) or comments.
|
|
94
|
+
#
|
|
95
|
+
# `kotlin` selects the build-file dialect: Kotlin (`.gradle.kts`) allows
|
|
96
|
+
# nested block comments but has no slashy strings, whereas Groovy
|
|
97
|
+
# (`.gradle`) has slashy strings but non-nested block comments.
|
|
98
|
+
sig { params(content: String, kotlin: T::Boolean).returns(String) }
|
|
99
|
+
def self.mask_literals_and_comments(content, kotlin: false)
|
|
100
|
+
masked = content.dup
|
|
101
|
+
index = 0
|
|
102
|
+
length = content.length
|
|
103
|
+
|
|
104
|
+
while index < length
|
|
105
|
+
stop = literal_or_comment_end(content, index, length, kotlin: kotlin)
|
|
106
|
+
if stop
|
|
107
|
+
mask_region!(masked, content, index, stop)
|
|
108
|
+
index = stop
|
|
109
|
+
else
|
|
110
|
+
index += 1
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
masked
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns the index just past a string literal or comment that starts at
|
|
118
|
+
# `index`, or `nil` when `index` is not the start of one.
|
|
119
|
+
sig { params(content: String, index: Integer, length: Integer, kotlin: T::Boolean).returns(T.nilable(Integer)) }
|
|
120
|
+
def self.literal_or_comment_end(content, index, length, kotlin: false) # rubocop:disable Metrics/PerceivedComplexity
|
|
121
|
+
three = content[index, 3]
|
|
122
|
+
two = content[index, 2]
|
|
123
|
+
char = T.must(content[index])
|
|
124
|
+
|
|
125
|
+
if three == '"""' || three == "'''"
|
|
126
|
+
close = content.index(three, index + 3)
|
|
127
|
+
close ? close + 3 : length
|
|
128
|
+
elsif !kotlin && two == "$/"
|
|
129
|
+
dollar_slashy_string_end(content, index, length)
|
|
130
|
+
elsif char == '"' || char == "'"
|
|
131
|
+
single_quote_string_end(content, index, char, length)
|
|
132
|
+
elsif two == "//"
|
|
133
|
+
content.index("\n", index) || length
|
|
134
|
+
elsif two == "/*"
|
|
135
|
+
block_comment_end(content, index, length, nested: kotlin)
|
|
136
|
+
elsif !kotlin && char == "/" && slashy_string_start?(content, index)
|
|
137
|
+
slashy_string_end(content, index, length)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Finds the index just past the closing `*/` of a block comment starting at
|
|
142
|
+
# `start_index`. Kotlin (`.gradle.kts`) permits nested `/*`/`*/` pairs;
|
|
143
|
+
# Groovy closes at the first `*/`.
|
|
144
|
+
sig { params(content: String, start_index: Integer, length: Integer, nested: T::Boolean).returns(Integer) }
|
|
145
|
+
def self.block_comment_end(content, start_index, length, nested: false)
|
|
146
|
+
depth = 1
|
|
147
|
+
stop = start_index + 2
|
|
148
|
+
while stop < length
|
|
149
|
+
pair = content[stop, 2]
|
|
150
|
+
if nested && pair == "/*"
|
|
151
|
+
depth += 1
|
|
152
|
+
stop += 2
|
|
153
|
+
elsif pair == "*/"
|
|
154
|
+
depth -= 1
|
|
155
|
+
stop += 2
|
|
156
|
+
return stop if depth.zero?
|
|
157
|
+
else
|
|
158
|
+
stop += 1
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
length
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Finds the index just past the closing quote of a single-quoted (`'` or
|
|
165
|
+
# `"`) string literal starting at `start_index`, honouring backslash escapes.
|
|
166
|
+
sig { params(content: String, start_index: Integer, quote: String, length: Integer).returns(Integer) }
|
|
167
|
+
def self.single_quote_string_end(content, start_index, quote, length)
|
|
168
|
+
stop = start_index + 1
|
|
169
|
+
while stop < length
|
|
170
|
+
char = content[stop]
|
|
171
|
+
if char == "\\"
|
|
172
|
+
stop += 2
|
|
173
|
+
elsif char == quote
|
|
174
|
+
return stop + 1
|
|
175
|
+
else
|
|
176
|
+
stop += 1
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
stop
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Finds the index just past the closing `/` of a Groovy slashy string
|
|
183
|
+
# (`/.../`) starting at `start_index`. Only `\/` escapes the delimiter.
|
|
184
|
+
sig { params(content: String, start_index: Integer, length: Integer).returns(Integer) }
|
|
185
|
+
def self.slashy_string_end(content, start_index, length)
|
|
186
|
+
stop = start_index + 1
|
|
187
|
+
while stop < length
|
|
188
|
+
if content[stop] == "\\" && content[stop + 1] == "/"
|
|
189
|
+
stop += 2
|
|
190
|
+
elsif content[stop] == "/"
|
|
191
|
+
return stop + 1
|
|
192
|
+
else
|
|
193
|
+
stop += 1
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
stop
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Finds the index just past the closing `/$` of a Groovy dollar-slashy
|
|
200
|
+
# string (`$/.../$`) starting at `start_index`. `$$` and `$/` are escapes.
|
|
201
|
+
sig { params(content: String, start_index: Integer, length: Integer).returns(Integer) }
|
|
202
|
+
def self.dollar_slashy_string_end(content, start_index, length)
|
|
203
|
+
stop = start_index + 2
|
|
204
|
+
while stop < length
|
|
205
|
+
pair = content[stop, 2]
|
|
206
|
+
if pair == "$$" || pair == "$/"
|
|
207
|
+
stop += 2
|
|
208
|
+
elsif pair == "/$"
|
|
209
|
+
return stop + 2
|
|
210
|
+
else
|
|
211
|
+
stop += 1
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
stop
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# A `/` begins a slashy string (rather than a division operator) only when
|
|
218
|
+
# a value is expected, i.e. the previous non-space character is not the end
|
|
219
|
+
# of an identifier, number, or closing bracket.
|
|
220
|
+
sig { params(content: String, index: Integer).returns(T::Boolean) }
|
|
221
|
+
def self.slashy_string_start?(content, index)
|
|
222
|
+
position = index - 1
|
|
223
|
+
position -= 1 while position >= 0 && (content[position] == " " || content[position] == "\t")
|
|
224
|
+
return true if position.negative?
|
|
225
|
+
|
|
226
|
+
!T.must(content[position]).match?(/[\w)\]}]/)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
sig { params(masked: String, original: String, start_index: Integer, stop_index: Integer).void }
|
|
230
|
+
def self.mask_region!(masked, original, start_index, stop_index)
|
|
231
|
+
(start_index...stop_index).each do |position|
|
|
232
|
+
masked[position] = original[position] == "\n" ? "\n" : " "
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
88
236
|
sig { returns(Ecosystem) }
|
|
89
237
|
def ecosystem
|
|
90
238
|
@ecosystem ||= T.let(
|
|
@@ -510,11 +658,22 @@ module Dependabot
|
|
|
510
658
|
|
|
511
659
|
sig { params(buildfile: Dependabot::DependencyFile).returns(String) }
|
|
512
660
|
def prepared_content(buildfile)
|
|
661
|
+
# Remove any dependencySubstitution blocks first, before the comment
|
|
662
|
+
# regexes below run. The coordinates inside `substitute module(...) using
|
|
663
|
+
# module(...)` rules are substitution targets, not real dependency
|
|
664
|
+
# declarations, and must not be updated. Braces inside strings/comments
|
|
665
|
+
# are masked so the matching closing brace is located correctly, and each
|
|
666
|
+
# block is deleted by its exact offsets. Doing this before the comment
|
|
667
|
+
# stripping keeps string literals intact so the masker can see them.
|
|
668
|
+
prepared_content = remove_dependency_substitution_blocks(
|
|
669
|
+
T.must(buildfile.content),
|
|
670
|
+
kotlin: buildfile.name.end_with?(".kts")
|
|
671
|
+
)
|
|
672
|
+
|
|
513
673
|
# Remove any comments
|
|
514
|
-
prepared_content =
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
.gsub(%r{(?<=^|\s)/\*.*?\*/}m, "")
|
|
674
|
+
prepared_content = prepared_content
|
|
675
|
+
.gsub(%r{(?<=^|\s)//.*$}, "\n")
|
|
676
|
+
.gsub(%r{(?<=^|\s)/\*.*?\*/}m, "")
|
|
518
677
|
|
|
519
678
|
# Remove the dependencyVerification section added by Gradle Witness
|
|
520
679
|
# (TODO: Support updating this in the FileUpdater)
|
|
@@ -527,6 +686,21 @@ module Dependabot
|
|
|
527
686
|
prepared_content
|
|
528
687
|
end
|
|
529
688
|
|
|
689
|
+
sig { params(content: String, kotlin: T::Boolean).returns(String) }
|
|
690
|
+
def remove_dependency_substitution_blocks(content, kotlin: false)
|
|
691
|
+
result = content.dup
|
|
692
|
+
masked = FileParser.mask_literals_and_comments(content, kotlin: kotlin)
|
|
693
|
+
block_ranges = T.let([], T::Array[T::Range[Integer]])
|
|
694
|
+
masked.to_enum(:scan, DEPENDENCY_SUBSTITUTION_DECLARATION_REGEX).each do
|
|
695
|
+
mtch = T.must(Regexp.last_match)
|
|
696
|
+
start_index = mtch.begin(0)
|
|
697
|
+
end_index = mtch.end(0) + closing_bracket_index(T.must(masked[mtch.end(0)..]))
|
|
698
|
+
block_ranges << (start_index..end_index)
|
|
699
|
+
end
|
|
700
|
+
block_ranges.reverse_each { |range| result[range] = "" }
|
|
701
|
+
result
|
|
702
|
+
end
|
|
703
|
+
|
|
530
704
|
sig { params(string: String).returns(Integer) }
|
|
531
705
|
def closing_bracket_index(string)
|
|
532
706
|
closes_required = 1
|
|
@@ -19,6 +19,12 @@ module Dependabot
|
|
|
19
19
|
|
|
20
20
|
SUPPORTED_BUILD_FILE_NAMES = %w(build.gradle build.gradle.kts).freeze
|
|
21
21
|
|
|
22
|
+
# Matches the start of a Gradle dependency substitution block, e.g.
|
|
23
|
+
# resolutionStrategy.dependencySubstitution {
|
|
24
|
+
# Coordinates inside such a block are substitution targets, not real
|
|
25
|
+
# dependency declarations, and must never be rewritten.
|
|
26
|
+
SUBSTITUTION_BLOCK_START_REGEX = /\bdependencySubstitution\s*\{/
|
|
27
|
+
|
|
22
28
|
sig { override.returns(T::Array[::Dependabot::DependencyFile]) }
|
|
23
29
|
def updated_dependency_files
|
|
24
30
|
updated_files = buildfiles.dup
|
|
@@ -108,9 +114,7 @@ module Dependabot
|
|
|
108
114
|
updated_files = wrapper_updater.update_files(buildfile)
|
|
109
115
|
replace_updated_files(files, updated_files)
|
|
110
116
|
end
|
|
111
|
-
|
|
112
|
-
update_lockfiles_for_buildfiles(files, buildfiles_processed)
|
|
113
|
-
end
|
|
117
|
+
update_lockfiles_for_buildfiles(files, buildfiles_processed)
|
|
114
118
|
|
|
115
119
|
files
|
|
116
120
|
end
|
|
@@ -254,31 +258,70 @@ module Dependabot
|
|
|
254
258
|
# single line.
|
|
255
259
|
file = T.must(requirement.file)
|
|
256
260
|
buildfile = T.must(buildfiles.find { |f| f.name == file })
|
|
261
|
+
content = T.must(buildfile.content)
|
|
262
|
+
substitution_ranges = substitution_block_line_ranges(content, kotlin: file.end_with?(".kts"))
|
|
263
|
+
|
|
264
|
+
content.lines.each_with_index.filter_map do |line, index|
|
|
265
|
+
next if substitution_ranges.any? { |range| range.cover?(index) }
|
|
257
266
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
line = line.gsub(%r{(?<=^|\s)//.*$}, "")
|
|
267
|
+
evaluated = evaluate_properties(line, buildfile)
|
|
268
|
+
evaluated = evaluated.gsub(%r{(?<=^|\s)//.*$}, "")
|
|
261
269
|
|
|
262
270
|
if dependency.name.include?(":")
|
|
263
271
|
dep_parts = dependency.name.split(":")
|
|
264
|
-
next
|
|
272
|
+
next unless evaluated.include?(T.must(dep_parts.first)) || evaluated.include?(T.must(dep_parts.last))
|
|
265
273
|
elsif file.end_with?(".properties")
|
|
266
274
|
property = requirement.source_string("property")
|
|
267
|
-
next
|
|
275
|
+
next unless property && evaluated.start_with?(property)
|
|
268
276
|
elsif file.end_with?(".toml")
|
|
269
|
-
next
|
|
277
|
+
next unless evaluated.include?(dependency.name)
|
|
270
278
|
else
|
|
271
279
|
name_regex_value = /['"]#{Regexp.quote(dependency.name)}['"]/
|
|
272
280
|
name_regex = /(id|kotlin)(\s+#{name_regex_value}|\(#{name_regex_value}\))/
|
|
273
|
-
next
|
|
281
|
+
next unless evaluated.match?(name_regex)
|
|
274
282
|
end
|
|
275
283
|
|
|
276
|
-
line.include?(T.must(requirement.requirement_string))
|
|
284
|
+
line if evaluated.include?(T.must(requirement.requirement_string))
|
|
277
285
|
end
|
|
278
286
|
end
|
|
279
287
|
# rubocop:enable Metrics/AbcSize
|
|
280
288
|
# rubocop:enable Metrics/PerceivedComplexity
|
|
281
289
|
|
|
290
|
+
# Returns the (0-based) line-index ranges covered by dependencySubstitution
|
|
291
|
+
# blocks, so lines inside them can be excluded regardless of formatting.
|
|
292
|
+
# Braces inside strings/comments are masked so the matching closing brace
|
|
293
|
+
# is located correctly.
|
|
294
|
+
sig { params(content: String, kotlin: T::Boolean).returns(T::Array[T::Range[Integer]]) }
|
|
295
|
+
def substitution_block_line_ranges(content, kotlin: false)
|
|
296
|
+
ranges = T.let([], T::Array[T::Range[Integer]])
|
|
297
|
+
masked = Gradle::FileParser.mask_literals_and_comments(content, kotlin: kotlin)
|
|
298
|
+
|
|
299
|
+
masked.to_enum(:scan, SUBSTITUTION_BLOCK_START_REGEX).each do
|
|
300
|
+
match = T.must(Regexp.last_match)
|
|
301
|
+
start_offset = match.begin(0)
|
|
302
|
+
close_offset = match.end(0) + closing_bracket_index(T.must(masked[match.end(0)..]))
|
|
303
|
+
|
|
304
|
+
start_line = T.must(masked[0...start_offset]).count("\n")
|
|
305
|
+
end_line = T.must(masked[0..close_offset]).count("\n")
|
|
306
|
+
ranges << (start_line..end_line)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
ranges
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
sig { params(string: String).returns(Integer) }
|
|
313
|
+
def closing_bracket_index(string)
|
|
314
|
+
closes_required = 1
|
|
315
|
+
|
|
316
|
+
string.chars.each_with_index do |char, index|
|
|
317
|
+
closes_required += 1 if char == "{"
|
|
318
|
+
closes_required -= 1 if char == "}"
|
|
319
|
+
return index if closes_required.zero?
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
0
|
|
323
|
+
end
|
|
324
|
+
|
|
282
325
|
sig { params(string: String, buildfile: Dependabot::DependencyFile).returns(String) }
|
|
283
326
|
def evaluate_properties(string, buildfile)
|
|
284
327
|
result = string.dup
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-gradle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.396.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
@@ -15,28 +15,28 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.396.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.396.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: dependabot-maven
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - '='
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.
|
|
32
|
+
version: 0.396.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - '='
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.
|
|
39
|
+
version: 0.396.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: debug
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -192,19 +192,19 @@ dependencies:
|
|
|
192
192
|
- !ruby/object:Gem::Version
|
|
193
193
|
version: '1.1'
|
|
194
194
|
- !ruby/object:Gem::Dependency
|
|
195
|
-
name:
|
|
195
|
+
name: turbo_tests2
|
|
196
196
|
requirement: !ruby/object:Gem::Requirement
|
|
197
197
|
requirements:
|
|
198
198
|
- - "~>"
|
|
199
199
|
- !ruby/object:Gem::Version
|
|
200
|
-
version:
|
|
200
|
+
version: 3.2.7
|
|
201
201
|
type: :development
|
|
202
202
|
prerelease: false
|
|
203
203
|
version_requirements: !ruby/object:Gem::Requirement
|
|
204
204
|
requirements:
|
|
205
205
|
- - "~>"
|
|
206
206
|
- !ruby/object:Gem::Version
|
|
207
|
-
version:
|
|
207
|
+
version: 3.2.7
|
|
208
208
|
- !ruby/object:Gem::Dependency
|
|
209
209
|
name: vcr
|
|
210
210
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -291,7 +291,7 @@ licenses:
|
|
|
291
291
|
- MIT
|
|
292
292
|
metadata:
|
|
293
293
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
294
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
294
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.396.0
|
|
295
295
|
rdoc_options: []
|
|
296
296
|
require_paths:
|
|
297
297
|
- lib
|