dependabot-gradle 0.394.0 → 0.395.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_parser.rb +178 -4
- data/lib/dependabot/gradle/file_updater.rb +53 -8
- 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: 5217463d5379b1769d013ce040af9eac03e0eb47e524505c86e1d3d67fc6de48
|
|
4
|
+
data.tar.gz: 739c83f7271742fe92942c8394c874c06c3d3b470003f6f636703d7ec76eeee6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2745a7a32084148fd05b96a323c0cc8ec428af6b06de8a8e12ad756b11c27f18f967f97475aa1eb1a143ffffe70e1ca06ca090651d4ed8b7ec85c9f9722b9ebf
|
|
7
|
+
data.tar.gz: 9273e6463194c037fab4594e48e92db6d727e1c7316501b43cca19930e6244aa2c9bdb3acff72e50f42fdb54b815d495381782ec1b527eac8d2d96dadba2441f
|
|
@@ -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
|
|
@@ -254,31 +260,70 @@ module Dependabot
|
|
|
254
260
|
# single line.
|
|
255
261
|
file = T.must(requirement.file)
|
|
256
262
|
buildfile = T.must(buildfiles.find { |f| f.name == file })
|
|
263
|
+
content = T.must(buildfile.content)
|
|
264
|
+
substitution_ranges = substitution_block_line_ranges(content, kotlin: file.end_with?(".kts"))
|
|
265
|
+
|
|
266
|
+
content.lines.each_with_index.filter_map do |line, index|
|
|
267
|
+
next if substitution_ranges.any? { |range| range.cover?(index) }
|
|
257
268
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
line = line.gsub(%r{(?<=^|\s)//.*$}, "")
|
|
269
|
+
evaluated = evaluate_properties(line, buildfile)
|
|
270
|
+
evaluated = evaluated.gsub(%r{(?<=^|\s)//.*$}, "")
|
|
261
271
|
|
|
262
272
|
if dependency.name.include?(":")
|
|
263
273
|
dep_parts = dependency.name.split(":")
|
|
264
|
-
next
|
|
274
|
+
next unless evaluated.include?(T.must(dep_parts.first)) || evaluated.include?(T.must(dep_parts.last))
|
|
265
275
|
elsif file.end_with?(".properties")
|
|
266
276
|
property = requirement.source_string("property")
|
|
267
|
-
next
|
|
277
|
+
next unless property && evaluated.start_with?(property)
|
|
268
278
|
elsif file.end_with?(".toml")
|
|
269
|
-
next
|
|
279
|
+
next unless evaluated.include?(dependency.name)
|
|
270
280
|
else
|
|
271
281
|
name_regex_value = /['"]#{Regexp.quote(dependency.name)}['"]/
|
|
272
282
|
name_regex = /(id|kotlin)(\s+#{name_regex_value}|\(#{name_regex_value}\))/
|
|
273
|
-
next
|
|
283
|
+
next unless evaluated.match?(name_regex)
|
|
274
284
|
end
|
|
275
285
|
|
|
276
|
-
line.include?(T.must(requirement.requirement_string))
|
|
286
|
+
line if evaluated.include?(T.must(requirement.requirement_string))
|
|
277
287
|
end
|
|
278
288
|
end
|
|
279
289
|
# rubocop:enable Metrics/AbcSize
|
|
280
290
|
# rubocop:enable Metrics/PerceivedComplexity
|
|
281
291
|
|
|
292
|
+
# Returns the (0-based) line-index ranges covered by dependencySubstitution
|
|
293
|
+
# blocks, so lines inside them can be excluded regardless of formatting.
|
|
294
|
+
# Braces inside strings/comments are masked so the matching closing brace
|
|
295
|
+
# is located correctly.
|
|
296
|
+
sig { params(content: String, kotlin: T::Boolean).returns(T::Array[T::Range[Integer]]) }
|
|
297
|
+
def substitution_block_line_ranges(content, kotlin: false)
|
|
298
|
+
ranges = T.let([], T::Array[T::Range[Integer]])
|
|
299
|
+
masked = Gradle::FileParser.mask_literals_and_comments(content, kotlin: kotlin)
|
|
300
|
+
|
|
301
|
+
masked.to_enum(:scan, SUBSTITUTION_BLOCK_START_REGEX).each do
|
|
302
|
+
match = T.must(Regexp.last_match)
|
|
303
|
+
start_offset = match.begin(0)
|
|
304
|
+
close_offset = match.end(0) + closing_bracket_index(T.must(masked[match.end(0)..]))
|
|
305
|
+
|
|
306
|
+
start_line = T.must(masked[0...start_offset]).count("\n")
|
|
307
|
+
end_line = T.must(masked[0..close_offset]).count("\n")
|
|
308
|
+
ranges << (start_line..end_line)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
ranges
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
sig { params(string: String).returns(Integer) }
|
|
315
|
+
def closing_bracket_index(string)
|
|
316
|
+
closes_required = 1
|
|
317
|
+
|
|
318
|
+
string.chars.each_with_index do |char, index|
|
|
319
|
+
closes_required += 1 if char == "{"
|
|
320
|
+
closes_required -= 1 if char == "}"
|
|
321
|
+
return index if closes_required.zero?
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
0
|
|
325
|
+
end
|
|
326
|
+
|
|
282
327
|
sig { params(string: String, buildfile: Dependabot::DependencyFile).returns(String) }
|
|
283
328
|
def evaluate_properties(string, buildfile)
|
|
284
329
|
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.395.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.395.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.395.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.395.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.395.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.395.0
|
|
295
295
|
rdoc_options: []
|
|
296
296
|
require_paths:
|
|
297
297
|
- lib
|