dependabot-gradle 0.389.0 → 0.391.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/distributions.rb +2 -2
- data/lib/dependabot/gradle/file_fetcher/settings_file_parser.rb +16 -0
- data/lib/dependabot/gradle/file_fetcher.rb +72 -5
- data/lib/dependabot/gradle/file_parser/property_value_finder.rb +1 -1
- data/lib/dependabot/gradle/file_parser/repositories_finder.rb +5 -5
- data/lib/dependabot/gradle/file_parser.rb +12 -18
- data/lib/dependabot/gradle/file_updater/lockfile_updater.rb +212 -19
- data/lib/dependabot/gradle/file_updater/wrapper/command_builder.rb +3 -3
- data/lib/dependabot/gradle/file_updater/wrapper/executing_version_detector.rb +2 -5
- data/lib/dependabot/gradle/file_updater/wrapper/properties_document.rb +2 -2
- data/lib/dependabot/gradle/file_updater/wrapper/properties_reconciler.rb +1 -4
- data/lib/dependabot/gradle/file_updater/wrapper_updater.rb +1 -1
- data/lib/dependabot/gradle/file_updater.rb +49 -82
- data/lib/dependabot/gradle/metadata_finder.rb +8 -7
- data/lib/dependabot/gradle/package/package_details_fetcher.rb +5 -5
- data/lib/dependabot/gradle/update_checker/multi_dependency_updater.rb +14 -14
- data/lib/dependabot/gradle/update_checker/requirements_updater.rb +30 -10
- data/lib/dependabot/gradle/update_checker.rb +9 -13
- data/lib/dependabot/gradle/version.rb +5 -8
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc95dfd45dcabce388680e617eaffd9ba19b84ef8a729cffd616cfb06ad4cb43
|
|
4
|
+
data.tar.gz: 67e2f381c9cabd09d54a93b0c153ad7e64a1344a652954e0d59a5008dfd7d56e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 31931d172c06b4afca6bb258af47b1e6420fc59c46ac3dc63d483e5c5e18395a775dcbe6fcd8ae77a38f611378e5cb4d84db26d26851ca52e36dc20413b4f220
|
|
7
|
+
data.tar.gz: b2faa2bcf5a79fb88d2e085ff504ad55028cfcd8f8f6851d54815a902137fa19b91eef51d1e1eb60c514d1f8e4f039b1d588f75d1d037b43aae58cf700863013
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "dependabot/dependency_requirement"
|
|
@@ -14,7 +14,7 @@ module Dependabot
|
|
|
14
14
|
sig { params(requirements: T::Array[Dependabot::DependencyRequirement]).returns(T::Boolean) }
|
|
15
15
|
def self.distribution_requirements?(requirements)
|
|
16
16
|
requirements.any? do |req|
|
|
17
|
-
req.
|
|
17
|
+
req.source_string("type") == DISTRIBUTION_DEPENDENCY_TYPE
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -33,6 +33,22 @@ module Dependabot
|
|
|
33
33
|
subprojects.uniq.map { |name| process_subproject_name(name) }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Returns a map of filesystem directory path => Gradle project name for all declared subprojects.
|
|
37
|
+
# For example: { 'app' => ':app', 'subprojects/chrome-trace' => ':chrome-trace' }
|
|
38
|
+
# This correctly handles custom projectDir mappings in settings files.
|
|
39
|
+
sig { returns(T::Hash[String, String]) }
|
|
40
|
+
def subproject_path_to_name_map
|
|
41
|
+
subprojects = T.let([], T::Array[String])
|
|
42
|
+
process_include_functions(subprojects)
|
|
43
|
+
subprojects.uniq.each_with_object(T.let({}, T::Hash[String, String])) do |name, map|
|
|
44
|
+
path = process_subproject_name(name)
|
|
45
|
+
next unless path
|
|
46
|
+
|
|
47
|
+
# Normalise name to Gradle colon-separated project path (e.g. ':app', ':sub:module')
|
|
48
|
+
map[path] = ":#{name.sub(/^:/, '')}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
36
52
|
private
|
|
37
53
|
|
|
38
54
|
sig { params(subprojects: T::Array[String]).void }
|
|
@@ -16,13 +16,13 @@ module Dependabot
|
|
|
16
16
|
require_relative "file_parser"
|
|
17
17
|
require_relative "file_fetcher/settings_file_parser"
|
|
18
18
|
|
|
19
|
-
SUPPORTED_LOCK_FILE_NAMES =
|
|
19
|
+
SUPPORTED_LOCK_FILE_NAMES = %w(gradle.lockfile).freeze
|
|
20
20
|
|
|
21
21
|
SUPPORTED_BUILD_FILE_NAMES =
|
|
22
|
-
|
|
22
|
+
%w(build.gradle build.gradle.kts).freeze
|
|
23
23
|
|
|
24
24
|
SUPPORTED_SETTINGS_FILE_NAMES =
|
|
25
|
-
|
|
25
|
+
%w(settings.gradle settings.gradle.kts).freeze
|
|
26
26
|
|
|
27
27
|
SUPPORTED_WRAPPER_FILES_PATH = %w(
|
|
28
28
|
gradlew
|
|
@@ -33,7 +33,10 @@ module Dependabot
|
|
|
33
33
|
|
|
34
34
|
# For now Gradle only supports library .toml files in the main gradle folder
|
|
35
35
|
SUPPORTED_VERSION_CATALOG_FILE_PATH =
|
|
36
|
-
|
|
36
|
+
%w(/gradle/libs.versions.toml).freeze
|
|
37
|
+
|
|
38
|
+
PLUGIN_SOURCE_SET_DIRS =
|
|
39
|
+
%w(src/main/java src/main/kotlin src/main/groovy src/main/resources).freeze
|
|
37
40
|
|
|
38
41
|
sig do
|
|
39
42
|
override
|
|
@@ -49,7 +52,7 @@ module Dependabot
|
|
|
49
52
|
def initialize(source:, credentials:, repo_contents_path: nil, options: {}, update_config: nil)
|
|
50
53
|
super
|
|
51
54
|
|
|
52
|
-
@lockfile_name = T.let(
|
|
55
|
+
@lockfile_name = T.let(SUPPORTED_LOCK_FILE_NAMES.first, String)
|
|
53
56
|
@buildfile_name = T.let(nil, T.nilable(String))
|
|
54
57
|
end
|
|
55
58
|
|
|
@@ -88,10 +91,74 @@ module Dependabot
|
|
|
88
91
|
files += subproject_buildfiles(root_dir)
|
|
89
92
|
files += subproject_lockfiles(root_dir)
|
|
90
93
|
files += dependency_script_plugins(root_dir)
|
|
94
|
+
files += convention_plugin_source_files(files, root_dir) if fetch_plugin_sources_for_lockfiles?
|
|
95
|
+
|
|
91
96
|
files + included_builds(root_dir)
|
|
92
97
|
.flat_map { |dir| all_buildfiles_in_build(dir) }
|
|
93
98
|
end
|
|
94
99
|
|
|
100
|
+
sig { returns(T::Boolean) }
|
|
101
|
+
def fetch_plugin_sources_for_lockfiles?
|
|
102
|
+
Dependabot::Experiments.enabled?(:gradle_lockfile_updater)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Only fetch source trees for *nested* project directories (e.g. "build-logic/convention"
|
|
106
|
+
# or "included/convention"), plus the root of a non-top-level build (e.g. "build-logic" or
|
|
107
|
+
# "buildSrc" itself), never the top-level repo root ("app" style modules of the outermost
|
|
108
|
+
# build). This targets the common convention-plugin module pattern - whether declared at an
|
|
109
|
+
# included/buildSrc build's own root or nested within it - without recursively scanning
|
|
110
|
+
# every ordinary top-level application module of the outermost build.
|
|
111
|
+
sig { params(files: T::Array[DependencyFile], root_dir: String).returns(T::Array[DependencyFile]) }
|
|
112
|
+
def convention_plugin_source_files(files, root_dir)
|
|
113
|
+
nested_plugin_project_dirs(files, root_dir).flat_map do |project_dir|
|
|
114
|
+
PLUGIN_SOURCE_SET_DIRS.flat_map do |relative_dir|
|
|
115
|
+
fetch_tree_support_files(clean_join([project_dir, relative_dir]))
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
sig { params(files: T::Array[DependencyFile], root_dir: String).returns(T::Array[String]) }
|
|
121
|
+
def nested_plugin_project_dirs(files, root_dir)
|
|
122
|
+
buildfile_dirs(files).select { |dir| plugin_source_eligible_dir?(dir, root_dir) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# A directory is eligible for convention-plugin source scanning if it's nested below the
|
|
126
|
+
# outermost repo root (contains a "/"), or if it's the own root of a build that is itself
|
|
127
|
+
# nested (i.e. root_dir isn't the outermost "."), such as an included build or buildSrc.
|
|
128
|
+
sig { params(dir: String, root_dir: String).returns(T::Boolean) }
|
|
129
|
+
def plugin_source_eligible_dir?(dir, root_dir)
|
|
130
|
+
return true if root_dir != "." && dir == root_dir
|
|
131
|
+
|
|
132
|
+
dir.include?("/")
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
sig { params(files: T::Array[DependencyFile]).returns(T::Array[String]) }
|
|
136
|
+
def buildfile_dirs(files)
|
|
137
|
+
files.filter_map do |file|
|
|
138
|
+
next unless SUPPORTED_BUILD_FILE_NAMES.include?(File.basename(file.name))
|
|
139
|
+
|
|
140
|
+
clean_join([File.dirname(file.name)])
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
sig { params(dir: String).returns(T::Array[DependencyFile]) }
|
|
145
|
+
def fetch_tree_support_files(dir)
|
|
146
|
+
entries = repo_contents(dir: dir, raise_errors: false)
|
|
147
|
+
return [] if entries.empty?
|
|
148
|
+
|
|
149
|
+
entries.flat_map do |entry|
|
|
150
|
+
entry_path = clean_join([dir, entry.name])
|
|
151
|
+
if entry.type == "dir"
|
|
152
|
+
fetch_tree_support_files(entry_path)
|
|
153
|
+
else
|
|
154
|
+
file = fetch_support_file(entry_path)
|
|
155
|
+
file ? [file] : []
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
rescue Dependabot::DependencyFileNotFound
|
|
159
|
+
[]
|
|
160
|
+
end
|
|
161
|
+
|
|
95
162
|
sig { params(root_dir: String).returns(T::Array[String]) }
|
|
96
163
|
def included_builds(root_dir)
|
|
97
164
|
builds = []
|
|
@@ -73,7 +73,7 @@ module Dependabot
|
|
|
73
73
|
|
|
74
74
|
sig { params(dependency_files: T::Array[Dependabot::DependencyFile]).void }
|
|
75
75
|
def initialize(dependency_files:)
|
|
76
|
-
@dependency_files =
|
|
76
|
+
@dependency_files = dependency_files
|
|
77
77
|
@properties = T.let({}, T::Hash[String, T::Hash[String, T::Hash[Symbol, String]]])
|
|
78
78
|
@top_level_buildfile = T.let(nil, T.nilable(Dependabot::DependencyFile))
|
|
79
79
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -11,8 +11,8 @@ module Dependabot
|
|
|
11
11
|
class RepositoriesFinder
|
|
12
12
|
extend T::Sig
|
|
13
13
|
|
|
14
|
-
SUPPORTED_BUILD_FILE_NAMES =
|
|
15
|
-
SUPPORTED_SETTINGS_FILE_NAMES =
|
|
14
|
+
SUPPORTED_BUILD_FILE_NAMES = %w(build.gradle build.gradle.kts).freeze
|
|
15
|
+
SUPPORTED_SETTINGS_FILE_NAMES = %w(settings.gradle settings.gradle.kts).freeze
|
|
16
16
|
|
|
17
17
|
# The Central Repo doesn't have special status for Gradle, but until
|
|
18
18
|
# we're confident we're selecting repos correctly it's wise to include
|
|
@@ -37,8 +37,8 @@ module Dependabot
|
|
|
37
37
|
).void
|
|
38
38
|
end
|
|
39
39
|
def initialize(dependency_files:, target_dependency_file:, credentials: [])
|
|
40
|
-
@dependency_files =
|
|
41
|
-
@credentials =
|
|
40
|
+
@dependency_files = dependency_files
|
|
41
|
+
@credentials = credentials
|
|
42
42
|
raise "No target file!" unless target_dependency_file
|
|
43
43
|
|
|
44
44
|
@target_dependency_file = T.let(target_dependency_file, Dependabot::DependencyFile)
|
|
@@ -28,28 +28,22 @@ module Dependabot
|
|
|
28
28
|
require_relative "file_parser/distributions_finder"
|
|
29
29
|
require_relative "file_parser/property_value_finder"
|
|
30
30
|
|
|
31
|
-
SUPPORTED_BUILD_FILE_NAMES =
|
|
32
|
-
%w(build.gradle build.gradle.kts settings.gradle settings.gradle.kts).freeze,
|
|
33
|
-
T::Array[String]
|
|
34
|
-
)
|
|
31
|
+
SUPPORTED_BUILD_FILE_NAMES = %w(build.gradle build.gradle.kts settings.gradle settings.gradle.kts).freeze
|
|
35
32
|
|
|
36
|
-
PROPERTY_REGEX =
|
|
37
|
-
/
|
|
33
|
+
PROPERTY_REGEX = /
|
|
38
34
|
(?:\$\{property\((?<property_name>[^:\s]*?)\)\})|
|
|
39
35
|
(?:\$\{(?<property_name>[^:\s]*?)\})|
|
|
40
36
|
(?:\$(?<property_name>[^:\s"']*))
|
|
41
|
-
/x
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
PLUGIN_BLOCK_DECLARATION_REGEX = T.let(/(?:^|\s)plugins\s*\{/, Regexp)
|
|
52
|
-
PLUGIN_ID_REGEX = T.let(/['"](?<id>#{PART})['"]/o, Regexp)
|
|
37
|
+
/x
|
|
38
|
+
|
|
39
|
+
PART = %r{[^\s,@'":/\\]+}
|
|
40
|
+
VSN_PART = %r{[^\s,'":/\\]+}
|
|
41
|
+
DEPENDENCY_DECLARATION_REGEX = /(?:\(|\s)\s*['"](?<declaration>#{PART}:#{PART}:#{VSN_PART})['"]/o
|
|
42
|
+
|
|
43
|
+
DEPENDENCY_SET_DECLARATION_REGEX = /(?:^|\s)dependencySet\((?<arguments>[^\)]+)\)\s*\{/
|
|
44
|
+
DEPENDENCY_SET_ENTRY_REGEX = /entry\s+['"](?<name>#{PART})['"]/o
|
|
45
|
+
PLUGIN_BLOCK_DECLARATION_REGEX = /(?:^|\s)plugins\s*\{/
|
|
46
|
+
PLUGIN_ID_REGEX = /['"](?<id>#{PART})['"]/o
|
|
53
47
|
|
|
54
48
|
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
|
55
49
|
def parse
|
|
@@ -7,6 +7,7 @@ require "shellwords"
|
|
|
7
7
|
require "sorbet-runtime"
|
|
8
8
|
|
|
9
9
|
require "dependabot/gradle/file_updater"
|
|
10
|
+
require "dependabot/gradle/file_fetcher/settings_file_parser"
|
|
10
11
|
|
|
11
12
|
module Dependabot
|
|
12
13
|
module Gradle
|
|
@@ -14,15 +15,29 @@ module Dependabot
|
|
|
14
15
|
class LockfileUpdater
|
|
15
16
|
extend T::Sig
|
|
16
17
|
|
|
17
|
-
INIT_SCRIPT_TASK_NAME =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
INIT_SCRIPT_TASK_NAME = "dependabotResolveAll"
|
|
19
|
+
GRADLE_JVMARGS_ATTEMPTS = [
|
|
20
|
+
"-Xmx1536m -Dfile.encoding=UTF-8",
|
|
21
|
+
"-Xmx2048m -Dfile.encoding=UTF-8"
|
|
22
|
+
].freeze
|
|
23
|
+
sig do
|
|
24
|
+
params(
|
|
25
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
|
26
|
+
repo_contents_path: T.nilable(String)
|
|
27
|
+
).void
|
|
28
|
+
end
|
|
29
|
+
def initialize(dependency_files:, repo_contents_path: nil)
|
|
21
30
|
@dependency_files = dependency_files
|
|
31
|
+
@repo_contents_path = repo_contents_path
|
|
22
32
|
end
|
|
23
33
|
|
|
24
|
-
sig
|
|
25
|
-
|
|
34
|
+
sig do
|
|
35
|
+
params(
|
|
36
|
+
build_file: Dependabot::DependencyFile,
|
|
37
|
+
build_files: T::Array[Dependabot::DependencyFile]
|
|
38
|
+
).returns(T::Array[Dependabot::DependencyFile])
|
|
39
|
+
end
|
|
40
|
+
def update_lockfiles(build_file, build_files: [build_file])
|
|
26
41
|
root_dir = determine_root_dir(build_file: build_file)
|
|
27
42
|
lockfiles = lockfiles_for_root(root_dir)
|
|
28
43
|
|
|
@@ -36,7 +51,7 @@ module Dependabot
|
|
|
36
51
|
cwd = File.join(temp_dir, root_dir == "/" ? "" : root_dir.delete_prefix("/"))
|
|
37
52
|
FileUtils.mkdir_p(cwd)
|
|
38
53
|
|
|
39
|
-
|
|
54
|
+
properties_file_path = File.join(cwd, "gradle.properties")
|
|
40
55
|
|
|
41
56
|
init_script_path = File.join(cwd, "dependabot-locking.init.gradle")
|
|
42
57
|
write_init_script(init_script_path)
|
|
@@ -44,12 +59,16 @@ module Dependabot
|
|
|
44
59
|
command_parts = [
|
|
45
60
|
gradle_executable_for(cwd: cwd, workspace_root: temp_dir.to_s),
|
|
46
61
|
"--init-script", init_script_path,
|
|
47
|
-
|
|
62
|
+
*lockfile_tasks_for(build_files),
|
|
48
63
|
"--write-locks",
|
|
49
64
|
"--no-daemon"
|
|
50
65
|
]
|
|
51
66
|
command = Shellwords.join(command_parts)
|
|
52
|
-
|
|
67
|
+
run_lockfile_update_with_retry(
|
|
68
|
+
command: command,
|
|
69
|
+
cwd: cwd,
|
|
70
|
+
properties_file_path: properties_file_path
|
|
71
|
+
)
|
|
53
72
|
|
|
54
73
|
update_lockfiles_content(temp_dir, lockfiles, updated_files)
|
|
55
74
|
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
@@ -171,6 +190,8 @@ module Dependabot
|
|
|
171
190
|
|
|
172
191
|
sig { params(temp_dir: T.any(Pathname, String)).void }
|
|
173
192
|
def populate_temp_directory(temp_dir)
|
|
193
|
+
copy_repo_contents_to_temp_dir(temp_dir, @repo_contents_path)
|
|
194
|
+
|
|
174
195
|
@dependency_files.each do |file|
|
|
175
196
|
# Handle "/" directory as root - File.join treats "/" as absolute path and ignores prior components
|
|
176
197
|
relative_dir = file.directory == "/" ? "" : file.directory
|
|
@@ -180,8 +201,33 @@ module Dependabot
|
|
|
180
201
|
end
|
|
181
202
|
end
|
|
182
203
|
|
|
183
|
-
sig { params(
|
|
184
|
-
def
|
|
204
|
+
sig { params(temp_dir: T.any(Pathname, String), repo_contents_path: T.nilable(String)).void }
|
|
205
|
+
def copy_repo_contents_to_temp_dir(temp_dir, repo_contents_path)
|
|
206
|
+
return if repo_contents_path.nil? || repo_contents_path.strip.empty?
|
|
207
|
+
|
|
208
|
+
source_dir = Pathname.new(repo_contents_path).expand_path
|
|
209
|
+
return unless source_dir.directory?
|
|
210
|
+
|
|
211
|
+
# Use the full checkout when available to ensure Gradle can compile
|
|
212
|
+
# convention plugin implementations from non-manifest source trees.
|
|
213
|
+
Dir.each_child(source_dir.to_s) do |entry|
|
|
214
|
+
entry = T.let(entry, String)
|
|
215
|
+
source_entry = File.join(source_dir.to_s, entry)
|
|
216
|
+
dest_entry = File.join(temp_dir.to_s, entry)
|
|
217
|
+
|
|
218
|
+
if entry == ".git"
|
|
219
|
+
# Some convention plugins shell out to `git` (e.g. for version derivation) and
|
|
220
|
+
# fail without a working repository. Symlink instead of copying to avoid the
|
|
221
|
+
# disk/time cost of duplicating the whole history for every lockfile update.
|
|
222
|
+
File.symlink(source_entry, dest_entry)
|
|
223
|
+
else
|
|
224
|
+
FileUtils.cp_r(source_entry, dest_entry)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
sig { params(file_name: String, jvmargs: String, base_content: String).void }
|
|
230
|
+
def write_properties_file(file_name, jvmargs:, base_content:) # rubocop:disable Metrics/PerceivedComplexity
|
|
185
231
|
http_proxy = ENV.fetch("HTTP_PROXY", nil)
|
|
186
232
|
https_proxy = ENV.fetch("HTTPS_PROXY", nil)
|
|
187
233
|
http_split = http_proxy&.split(":")
|
|
@@ -191,9 +237,20 @@ module Dependabot
|
|
|
191
237
|
http_proxy_port = http_split&.fetch(2) || "1080"
|
|
192
238
|
https_proxy_port = https_split&.fetch(2) || "1080"
|
|
193
239
|
|
|
194
|
-
|
|
240
|
+
existing_jvmargs = base_content[/^org\.gradle\.jvmargs=(.*)$/, 1]
|
|
241
|
+
# Preserve any existing jvmargs (e.g. --add-opens flags) by merging rather than
|
|
242
|
+
# replacing them outright; the raw line is dropped from existing_content below
|
|
243
|
+
# since combined_jvmargs already includes its value.
|
|
244
|
+
has_existing_jvmargs = existing_jvmargs && !existing_jvmargs.strip.empty?
|
|
245
|
+
combined_jvmargs = has_existing_jvmargs ? "#{existing_jvmargs.strip} #{jvmargs}" : jvmargs
|
|
246
|
+
existing_content = base_content.gsub(/^org\.gradle\.jvmargs=.*$\n?/, "")
|
|
195
247
|
|
|
196
248
|
proxy_properties = "
|
|
249
|
+
org.gradle.jvmargs=#{combined_jvmargs}
|
|
250
|
+
org.gradle.workers.max=1
|
|
251
|
+
org.gradle.java.installations.auto-download=false
|
|
252
|
+
org.gradle.java.installations.paths=#{installed_gradle_toolchain_paths}
|
|
253
|
+
kotlin.compiler.execution.strategy=in-process
|
|
197
254
|
systemProp.http.proxyHost=#{http_proxy_host}
|
|
198
255
|
systemProp.http.proxyPort=#{http_proxy_port}
|
|
199
256
|
systemProp.https.proxyHost=#{https_proxy_host}
|
|
@@ -203,24 +260,160 @@ systemProp.https.proxyPort=#{https_proxy_port}"
|
|
|
203
260
|
File.write(file_name, existing_content + separator + proxy_properties)
|
|
204
261
|
end
|
|
205
262
|
|
|
263
|
+
sig { params(command: String, cwd: String, properties_file_path: String).void }
|
|
264
|
+
def run_lockfile_update_with_retry(command:, cwd:, properties_file_path:)
|
|
265
|
+
base_content = File.exist?(properties_file_path) ? File.read(properties_file_path) : ""
|
|
266
|
+
|
|
267
|
+
catch(:success) do
|
|
268
|
+
GRADLE_JVMARGS_ATTEMPTS.each_with_index do |jvmargs, index|
|
|
269
|
+
write_properties_file(properties_file_path, jvmargs: jvmargs, base_content: base_content)
|
|
270
|
+
begin
|
|
271
|
+
SharedHelpers.run_shell_command(command, cwd: cwd)
|
|
272
|
+
throw :success
|
|
273
|
+
rescue SharedHelpers::HelperSubprocessFailed => e
|
|
274
|
+
# If this is the last attempt, re-raise the error
|
|
275
|
+
raise e if index == GRADLE_JVMARGS_ATTEMPTS.length - 1
|
|
276
|
+
|
|
277
|
+
# Only retry if it's a retryable failure
|
|
278
|
+
raise e unless retryable_daemon_failure?(e)
|
|
279
|
+
|
|
280
|
+
Dependabot.logger.warn(
|
|
281
|
+
"Gradle build failed with jvmargs '#{jvmargs}'. Retrying once with larger heap size..."
|
|
282
|
+
)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
sig { params(error: SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
|
|
289
|
+
def retryable_daemon_failure?(error)
|
|
290
|
+
message = error.message.downcase
|
|
291
|
+
# Retry if daemon disappeared
|
|
292
|
+
if message.include?("daemon disappeared") || message.include?("build daemon disappeared unexpectedly")
|
|
293
|
+
return true
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Retry if process was killed (SIGKILL)
|
|
297
|
+
return true if T.cast(error.error_context[:process_termsig], T.nilable(Integer)) == SharedHelpers::SIGKILL
|
|
298
|
+
|
|
299
|
+
false
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
sig { returns(String) }
|
|
303
|
+
def installed_gradle_toolchain_paths
|
|
304
|
+
Dir.glob("/usr/lib/jvm/java-*-openjdk-*").join(",")
|
|
305
|
+
end
|
|
306
|
+
|
|
206
307
|
sig { params(file_name: String).void }
|
|
207
308
|
def write_init_script(file_name)
|
|
208
|
-
# Resolve
|
|
209
|
-
#
|
|
309
|
+
# Resolve every lockable configuration in one invocation. Not from the task action: that
|
|
310
|
+
# needs the project, which the configuration cache forbids.
|
|
210
311
|
script_content = <<~GRADLE
|
|
211
312
|
allprojects {
|
|
212
313
|
if (tasks.findByName("#{INIT_SCRIPT_TASK_NAME}") == null) {
|
|
213
|
-
tasks.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
314
|
+
tasks.create("#{INIT_SCRIPT_TASK_NAME}") { }
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
gradle.projectsEvaluated {
|
|
319
|
+
// Prepare every project before resolving anything: with a project dependency,
|
|
320
|
+
// resolving one project computes the other project's javaCompiler.
|
|
321
|
+
#{compiler_fallback_snippet}
|
|
322
|
+
|
|
323
|
+
gradle.rootProject.allprojects { target ->
|
|
324
|
+
target.configurations.findAll {
|
|
325
|
+
it.canBeResolved &&
|
|
326
|
+
it.resolutionStrategy.dependencyLockingEnabled &&
|
|
327
|
+
it.allDependencies.any { dependency ->
|
|
328
|
+
dependency instanceof org.gradle.api.artifacts.ModuleDependency
|
|
329
|
+
}
|
|
330
|
+
}.each { it.incoming.resolutionResult.allDependencies }
|
|
218
331
|
}
|
|
219
332
|
}
|
|
220
333
|
GRADLE
|
|
221
334
|
File.write(file_name, script_content)
|
|
222
335
|
end
|
|
223
336
|
|
|
337
|
+
# Resolving computes the compile task's javaCompiler, which fails when the toolchain is
|
|
338
|
+
# missing. Only the compiler is swapped, so resolution is unaffected.
|
|
339
|
+
sig { returns(String) }
|
|
340
|
+
def compiler_fallback_snippet
|
|
341
|
+
<<~GRADLE.gsub(/^/, " ")
|
|
342
|
+
gradle.rootProject.allprojects { target ->
|
|
343
|
+
// By name: JavaToolchainService only exists from 6.7, locking from 4.8.
|
|
344
|
+
def javaExtension = target.extensions.findByName("java")
|
|
345
|
+
def toolchainService = target.extensions.findByName("javaToolchains")
|
|
346
|
+
if (javaExtension != null && toolchainService != null) {
|
|
347
|
+
def toolchain = javaExtension.toolchain
|
|
348
|
+
// Toolchains landed in 6.7, vendor and implementation in 6.8, native image in 8.14.
|
|
349
|
+
def constraints = ["vendor", "implementation", "nativeImageCapable"].collectEntries { name ->
|
|
350
|
+
[(name): toolchain.hasProperty(name) ? toolchain."$name".getOrNull() : null]
|
|
351
|
+
}
|
|
352
|
+
if (toolchain.languageVersion.present) {
|
|
353
|
+
def requested = toolchain.languageVersion.get()
|
|
354
|
+
try {
|
|
355
|
+
// Throwaway spec: compilerFor finalizes whatever spec it is given.
|
|
356
|
+
toolchainService.compilerFor { spec ->
|
|
357
|
+
spec.languageVersion.set(requested)
|
|
358
|
+
constraints.each { name, value -> if (value != null) spec."$name".set(value) }
|
|
359
|
+
}.get()
|
|
360
|
+
} catch (Exception ignored) {
|
|
361
|
+
def fallback = JavaLanguageVersion.of(JavaVersion.current().majorVersion)
|
|
362
|
+
target.logger.lifecycle("Dependabot: Java toolchain " + requested +
|
|
363
|
+
" is not available, compiling with " + fallback + " to resolve dependencies.")
|
|
364
|
+
def c = toolchainService.compilerFor { s -> s.languageVersion.set(fallback) }
|
|
365
|
+
target.tasks.withType(JavaCompile).configureEach { t -> t.javaCompiler.set(c) }
|
|
366
|
+
// Kotlin asks for its own toolchain; convention plugins compile during config.
|
|
367
|
+
def l = toolchainService.launcherFor { s -> s.languageVersion.set(fallback) }
|
|
368
|
+
target.tasks.matching { it.hasProperty("kotlinJavaToolchain") }
|
|
369
|
+
.configureEach { t -> t.kotlinJavaToolchain.toolchain.use(l) }
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
GRADLE
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
sig { params(build_files: T::Array[Dependabot::DependencyFile]).returns(T::Array[String]) }
|
|
378
|
+
def lockfile_tasks_for(build_files)
|
|
379
|
+
build_files.filter_map { |build_file| dependency_task_for(build_file) }.uniq << INIT_SCRIPT_TASK_NAME
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
sig { params(build_file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
|
383
|
+
def dependency_task_for(build_file)
|
|
384
|
+
settings_file = find_settings_file(build_file)
|
|
385
|
+
return nil unless settings_file
|
|
386
|
+
|
|
387
|
+
root_dir = determine_root_dir(build_file: build_file)
|
|
388
|
+
file_path = normalized_file_path(build_file)
|
|
389
|
+
relative_path = path_relative_to_root(file_path, root_dir)
|
|
390
|
+
|
|
391
|
+
return nil if relative_path.start_with?("gradle/")
|
|
392
|
+
|
|
393
|
+
dirname = File.dirname(relative_path)
|
|
394
|
+
return nil if dirname == "." || dirname.empty?
|
|
395
|
+
|
|
396
|
+
# Look up the canonical Gradle project name from the settings file to handle repos
|
|
397
|
+
# that use custom projectDir mappings
|
|
398
|
+
# (e.g. project(':chrome-trace').projectDir = file('subprojects/chrome-trace')).
|
|
399
|
+
# Deriving the task from the filesystem path alone would produce a wrong Gradle task path.
|
|
400
|
+
parser = FileFetcher::SettingsFileParser.new(settings_file: settings_file)
|
|
401
|
+
project_name = parser.subproject_path_to_name_map[dirname]
|
|
402
|
+
return nil unless project_name
|
|
403
|
+
|
|
404
|
+
"#{project_name}:dependencies"
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
sig { params(file_path: String, root_dir: String).returns(String) }
|
|
408
|
+
def path_relative_to_root(file_path, root_dir)
|
|
409
|
+
return file_path.sub(%r{^/}, "") if root_dir == "/"
|
|
410
|
+
|
|
411
|
+
root_prefix = "#{root_dir}/"
|
|
412
|
+
return file_path.delete_prefix(root_prefix) if file_path.start_with?(root_prefix)
|
|
413
|
+
|
|
414
|
+
file_path.sub(%r{^/}, "")
|
|
415
|
+
end
|
|
416
|
+
|
|
224
417
|
sig { params(cwd: String, workspace_root: String).returns(String) }
|
|
225
418
|
def gradle_executable_for(cwd:, workspace_root:)
|
|
226
419
|
cwd_path = Pathname.new(cwd).expand_path
|
|
@@ -79,19 +79,19 @@ module Dependabot
|
|
|
79
79
|
|
|
80
80
|
sig { returns(String) }
|
|
81
81
|
def version
|
|
82
|
-
T.
|
|
82
|
+
T.must(T.must(@requirements[0]).requirement_string)
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
sig { returns(T.nilable(String)) }
|
|
86
86
|
def checksum
|
|
87
87
|
return nil unless @requirements.size > 1
|
|
88
88
|
|
|
89
|
-
T.
|
|
89
|
+
T.must(T.must(@requirements[1]).requirement_string)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
sig { returns(T.nilable(String)) }
|
|
93
93
|
def distribution_type
|
|
94
|
-
url = T.
|
|
94
|
+
url = T.must(@requirements[0]).source_string("url")
|
|
95
95
|
# Anchor to the `-bin.zip` / `-all.zip` filename suffix so a path segment such as a
|
|
96
96
|
# mirror host (e.g. https://binaries.example.com/...) can't false-match `bin`/`all`.
|
|
97
97
|
url&.match(/-(bin|all)\.zip/)&.captures&.first
|
|
@@ -23,13 +23,10 @@ module Dependabot
|
|
|
23
23
|
# Anchored to the `gradle-<version>-(bin|all).zip` filename so host/port numbers in custom
|
|
24
24
|
# mirror URLs are never mistaken for the version. The captured token is validated with
|
|
25
25
|
# Version.correct? so non-version matches (and RC/milestone names) are handled safely.
|
|
26
|
-
DISTRIBUTION_URL_VERSION_REGEX =
|
|
27
|
-
/gradle-(?<version>.+?)-(?:bin|all)\.zip/,
|
|
28
|
-
Regexp
|
|
29
|
-
)
|
|
26
|
+
DISTRIBUTION_URL_VERSION_REGEX = /gradle-(?<version>.+?)-(?:bin|all)\.zip/
|
|
30
27
|
|
|
31
28
|
# Matches the version printed by `gradle --version`, e.g. "Gradle 9.2.1".
|
|
32
|
-
GRADLE_VERSION_OUTPUT_REGEX =
|
|
29
|
+
GRADLE_VERSION_OUTPUT_REGEX = /^Gradle\s+(?<version>\d+(?:\.\d+){1,2}(?:-[\w.]+)?)/
|
|
33
30
|
|
|
34
31
|
sig { params(distribution_url: T.nilable(String)).returns(T.nilable(Dependabot::Gradle::Version)) }
|
|
35
32
|
def self.from_distribution_url(distribution_url)
|
|
@@ -29,8 +29,8 @@ module Dependabot
|
|
|
29
29
|
|
|
30
30
|
# Properties files use `=`, `:` or whitespace as the key/value separator. Gradle always
|
|
31
31
|
# writes `=`, but we parse all three so user-authored files are handled faithfully.
|
|
32
|
-
KEY_VALUE_REGEX =
|
|
33
|
-
COMMENT_REGEX =
|
|
32
|
+
KEY_VALUE_REGEX = /\A(\s*)([^\s:=]+)(\s*[:=]\s*|\s+)(.*)\z/
|
|
33
|
+
COMMENT_REGEX = /\A\s*[#!]/
|
|
34
34
|
|
|
35
35
|
sig { params(content: String).returns(PropertiesDocument) }
|
|
36
36
|
def self.parse(content)
|
|
@@ -22,10 +22,7 @@ module Dependabot
|
|
|
22
22
|
|
|
23
23
|
# Keys whose value is owned by the update itself and therefore taken from the regenerated
|
|
24
24
|
# file. Everything not listed here is preserved verbatim from the user's original file.
|
|
25
|
-
MANAGED_KEYS =
|
|
26
|
-
%w(distributionUrl distributionSha256Sum).freeze,
|
|
27
|
-
T::Array[String]
|
|
28
|
-
)
|
|
25
|
+
MANAGED_KEYS = %w(distributionUrl distributionSha256Sum).freeze
|
|
29
26
|
|
|
30
27
|
# Reconciles the regenerated wrapper properties back onto the user's original file.
|
|
31
28
|
#
|
|
@@ -59,7 +59,7 @@ module Dependabot
|
|
|
59
59
|
|
|
60
60
|
# we only run this updater if the build file has a requirement for this dependency
|
|
61
61
|
target_requirements = dependency.requirements.select do |req|
|
|
62
|
-
|
|
62
|
+
req.file == build_file.name
|
|
63
63
|
end
|
|
64
64
|
return [] unless target_requirements.any?
|
|
65
65
|
|
|
@@ -17,7 +17,7 @@ module Dependabot
|
|
|
17
17
|
require_relative "file_updater/lockfile_updater"
|
|
18
18
|
require_relative "file_updater/wrapper_updater"
|
|
19
19
|
|
|
20
|
-
SUPPORTED_BUILD_FILE_NAMES = %w(build.gradle build.gradle.kts
|
|
20
|
+
SUPPORTED_BUILD_FILE_NAMES = %w(build.gradle build.gradle.kts).freeze
|
|
21
21
|
|
|
22
22
|
sig { override.returns(T::Array[::Dependabot::DependencyFile]) }
|
|
23
23
|
def updated_dependency_files
|
|
@@ -74,10 +74,10 @@ module Dependabot
|
|
|
74
74
|
.reject { |new_req, old_req| new_req == old_req }
|
|
75
75
|
# Loop through each changed requirement and update the buildfiles
|
|
76
76
|
reqs.each do |new_req, old_req|
|
|
77
|
-
raise "Bad req match" if old_req.nil? ||
|
|
78
|
-
next if
|
|
77
|
+
raise "Bad req match" if old_req.nil? || new_req.file != old_req.file
|
|
78
|
+
next if new_req.requirement == old_req.requirement
|
|
79
79
|
|
|
80
|
-
buildfile = files.find { |f| f.name ==
|
|
80
|
+
buildfile = files.find { |f| f.name == new_req.file }
|
|
81
81
|
|
|
82
82
|
# Currently, Dependabot assumes that Gradle projects using Gradle submodules are all in a single
|
|
83
83
|
# repo. However, some projects are actually using git submodule references for the Gradle submodules.
|
|
@@ -88,16 +88,18 @@ module Dependabot
|
|
|
88
88
|
|
|
89
89
|
raise DependencyFileNotResolvable, "No build file found to update the dependency" if buildfile.nil?
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
if metadata && metadata[:property_name].is_a?(String)
|
|
91
|
+
if new_req.metadata_string("property_name")
|
|
93
92
|
files = update_files_for_property_change(files, old_req, new_req)
|
|
94
|
-
elsif
|
|
93
|
+
elsif new_req.metadata_string_hash("dependency_set")
|
|
95
94
|
files = update_files_for_dep_set_change(files, old_req, new_req)
|
|
96
95
|
else
|
|
97
96
|
files[T.must(files.index(buildfile))] = update_version_in_buildfile(dependency, buildfile, old_req, new_req)
|
|
98
97
|
end
|
|
99
98
|
|
|
100
|
-
|
|
99
|
+
# Always look up the updated buildfile from files after any modification
|
|
100
|
+
# to ensure buildfiles_processed contains the latest version
|
|
101
|
+
updated_buildfile = files.find { |f| f.name == buildfile.name }
|
|
102
|
+
buildfiles_processed[buildfile.name] = T.must(updated_buildfile)
|
|
101
103
|
end
|
|
102
104
|
|
|
103
105
|
# runs native updaters (e.g. wrapper, lockfile) on relevant build files updated
|
|
@@ -122,17 +124,28 @@ module Dependabot
|
|
|
122
124
|
).void
|
|
123
125
|
end
|
|
124
126
|
def update_lockfiles_for_buildfiles(files, buildfiles_processed)
|
|
125
|
-
|
|
127
|
+
# Support files (e.g. fetched convention plugin sources) are excluded from
|
|
128
|
+
# `buildfiles`/`files`, but Gradle still needs them on disk to compile
|
|
129
|
+
# plugins referenced by included/buildSrc builds, so pass them through too.
|
|
130
|
+
support_files = dependency_files.select(&:support_file?)
|
|
131
|
+
lockfile_updater = LockfileUpdater.new(
|
|
132
|
+
dependency_files: files + support_files,
|
|
133
|
+
repo_contents_path: repo_contents_path
|
|
134
|
+
)
|
|
135
|
+
buildfiles_by_root = T.let({}, T::Hash[String, T::Array[Dependabot::DependencyFile]])
|
|
126
136
|
|
|
127
137
|
buildfiles_processed.each_value do |buildfile|
|
|
128
|
-
lockfile_updater = LockfileUpdater.new(dependency_files: files)
|
|
129
138
|
root_dir = lockfile_updater.determine_root_dir(build_file: buildfile)
|
|
130
|
-
|
|
139
|
+
buildfiles_by_root[root_dir] ||= []
|
|
140
|
+
T.must(buildfiles_by_root[root_dir]) << buildfile
|
|
141
|
+
end
|
|
131
142
|
|
|
132
|
-
|
|
143
|
+
buildfiles_by_root.each_value do |buildfiles|
|
|
144
|
+
updated_files = lockfile_updater.update_lockfiles(T.must(buildfiles.first), build_files: buildfiles)
|
|
133
145
|
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
# Support files are only needed to populate Gradle's working directory;
|
|
147
|
+
# they must never be reintroduced into the buildfiles-only result set.
|
|
148
|
+
replace_updated_files(files, updated_files.reject(&:support_file?))
|
|
136
149
|
end
|
|
137
150
|
end
|
|
138
151
|
sig do
|
|
@@ -156,47 +169,45 @@ module Dependabot
|
|
|
156
169
|
sig do
|
|
157
170
|
params(
|
|
158
171
|
buildfiles: T::Array[Dependabot::DependencyFile],
|
|
159
|
-
old_req:
|
|
160
|
-
new_req:
|
|
172
|
+
old_req: Dependabot::DependencyRequirement,
|
|
173
|
+
new_req: Dependabot::DependencyRequirement
|
|
161
174
|
)
|
|
162
175
|
.returns(T::Array[Dependabot::DependencyFile])
|
|
163
176
|
end
|
|
164
177
|
def update_files_for_property_change(buildfiles, old_req, new_req)
|
|
165
178
|
files = buildfiles.dup
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
file = string_value(new_req, :file)
|
|
179
|
+
property_name = T.must(new_req.metadata_string("property_name"))
|
|
180
|
+
file = T.must(new_req.file)
|
|
169
181
|
buildfile = T.must(files.find { |f| f.name == file })
|
|
170
182
|
|
|
171
183
|
PropertyValueUpdater.new(dependency_files: files)
|
|
172
184
|
.update_files_for_property_change(
|
|
173
185
|
property_name: property_name,
|
|
174
186
|
callsite_buildfile: buildfile,
|
|
175
|
-
previous_value:
|
|
176
|
-
updated_value:
|
|
187
|
+
previous_value: T.must(old_req.requirement_string),
|
|
188
|
+
updated_value: T.must(new_req.requirement_string)
|
|
177
189
|
)
|
|
178
190
|
end
|
|
179
191
|
|
|
180
192
|
sig do
|
|
181
193
|
params(
|
|
182
194
|
buildfiles: T::Array[Dependabot::DependencyFile],
|
|
183
|
-
old_req:
|
|
184
|
-
new_req:
|
|
195
|
+
old_req: Dependabot::DependencyRequirement,
|
|
196
|
+
new_req: Dependabot::DependencyRequirement
|
|
185
197
|
)
|
|
186
198
|
.returns(T::Array[Dependabot::DependencyFile])
|
|
187
199
|
end
|
|
188
200
|
def update_files_for_dep_set_change(buildfiles, old_req, new_req)
|
|
189
201
|
files = buildfiles.dup
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
buildfile = T.must(files.find { |f| f.name == string_value(new_req, :file) })
|
|
202
|
+
dependency_set = T.must(new_req.metadata_string_hash("dependency_set"))
|
|
203
|
+
buildfile = T.must(files.find { |f| f.name == new_req.file })
|
|
193
204
|
|
|
194
205
|
DependencySetUpdater.new(dependency_files: files)
|
|
195
206
|
.update_files_for_dep_set_change(
|
|
196
207
|
dependency_set: dependency_set,
|
|
197
208
|
buildfile: buildfile,
|
|
198
|
-
previous_requirement:
|
|
199
|
-
updated_requirement:
|
|
209
|
+
previous_requirement: T.must(old_req.requirement_string),
|
|
210
|
+
updated_requirement: T.must(new_req.requirement_string)
|
|
200
211
|
)
|
|
201
212
|
end
|
|
202
213
|
|
|
@@ -204,8 +215,8 @@ module Dependabot
|
|
|
204
215
|
params(
|
|
205
216
|
dependency: Dependabot::Dependency,
|
|
206
217
|
buildfile: Dependabot::DependencyFile,
|
|
207
|
-
previous_req:
|
|
208
|
-
requirement:
|
|
218
|
+
previous_req: Dependabot::DependencyRequirement,
|
|
219
|
+
requirement: Dependabot::DependencyRequirement
|
|
209
220
|
)
|
|
210
221
|
.returns(Dependabot::DependencyFile)
|
|
211
222
|
end
|
|
@@ -235,13 +246,13 @@ module Dependabot
|
|
|
235
246
|
sig do
|
|
236
247
|
params(
|
|
237
248
|
dependency: Dependabot::Dependency,
|
|
238
|
-
requirement:
|
|
249
|
+
requirement: Dependabot::DependencyRequirement
|
|
239
250
|
).returns(T::Array[String])
|
|
240
251
|
end
|
|
241
252
|
def original_buildfile_declarations(dependency, requirement)
|
|
242
253
|
# This implementation is limited to declarations that appear on a
|
|
243
254
|
# single line.
|
|
244
|
-
file =
|
|
255
|
+
file = T.must(requirement.file)
|
|
245
256
|
buildfile = T.must(buildfiles.find { |f| f.name == file })
|
|
246
257
|
|
|
247
258
|
T.must(buildfile.content).lines.select do |line|
|
|
@@ -252,11 +263,7 @@ module Dependabot
|
|
|
252
263
|
dep_parts = dependency.name.split(":")
|
|
253
264
|
next false unless line.include?(T.must(dep_parts.first)) || line.include?(T.must(dep_parts.last))
|
|
254
265
|
elsif file.end_with?(".properties")
|
|
255
|
-
|
|
256
|
-
property = T.let(nil, T.nilable(String))
|
|
257
|
-
source_hash = symbol_object_hash(source)
|
|
258
|
-
source_property = source_hash&.[](:property)
|
|
259
|
-
property = source_property if source_property.is_a?(String)
|
|
266
|
+
property = requirement.source_string("property")
|
|
260
267
|
next false unless property && line.start_with?(property)
|
|
261
268
|
elsif file.end_with?(".toml")
|
|
262
269
|
next false unless line.include?(dependency.name)
|
|
@@ -266,7 +273,7 @@ module Dependabot
|
|
|
266
273
|
next false unless line.match?(name_regex)
|
|
267
274
|
end
|
|
268
275
|
|
|
269
|
-
line.include?(
|
|
276
|
+
line.include?(T.must(requirement.requirement_string))
|
|
270
277
|
end
|
|
271
278
|
end
|
|
272
279
|
# rubocop:enable Metrics/AbcSize
|
|
@@ -301,57 +308,17 @@ module Dependabot
|
|
|
301
308
|
sig do
|
|
302
309
|
params(
|
|
303
310
|
original_buildfile_declaration: String,
|
|
304
|
-
previous_req:
|
|
305
|
-
requirement:
|
|
311
|
+
previous_req: Dependabot::DependencyRequirement,
|
|
312
|
+
requirement: Dependabot::DependencyRequirement
|
|
306
313
|
).returns(String)
|
|
307
314
|
end
|
|
308
315
|
def updated_buildfile_declaration(original_buildfile_declaration, previous_req, requirement)
|
|
309
|
-
original_req_string =
|
|
310
|
-
new_req_string =
|
|
316
|
+
original_req_string = T.must(previous_req.requirement_string)
|
|
317
|
+
new_req_string = T.must(requirement.requirement_string)
|
|
311
318
|
|
|
312
319
|
original_buildfile_declaration.gsub(original_req_string, new_req_string)
|
|
313
320
|
end
|
|
314
321
|
|
|
315
|
-
sig { params(hash: T::Hash[Symbol, Object], key: Symbol).returns(String) }
|
|
316
|
-
def string_value(hash, key)
|
|
317
|
-
value = hash.fetch(key)
|
|
318
|
-
raise TypeError, "Expected #{key} to be a String" unless value.is_a?(String)
|
|
319
|
-
|
|
320
|
-
value
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
sig { params(hash: T::Hash[Symbol, Object], key: Symbol).returns(T::Hash[Symbol, Object]) }
|
|
324
|
-
def symbol_object_hash_value(hash, key)
|
|
325
|
-
value = hash.fetch(key)
|
|
326
|
-
raise TypeError, "Expected #{key} to be a Hash" unless value.is_a?(Hash)
|
|
327
|
-
|
|
328
|
-
value
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
sig { params(hash: T::Hash[Symbol, Object], key: Symbol).returns(T::Hash[Symbol, String]) }
|
|
332
|
-
def symbol_string_hash_value(hash, key)
|
|
333
|
-
value = hash.fetch(key)
|
|
334
|
-
raise TypeError, "Expected #{key} to be a Hash" unless value.is_a?(Hash)
|
|
335
|
-
|
|
336
|
-
result = T.let({}, T::Hash[Symbol, String])
|
|
337
|
-
value.each_pair do |hash_key_object, hash_value_object|
|
|
338
|
-
hash_key = T.cast(hash_key_object, T.nilable(Object))
|
|
339
|
-
hash_value = T.cast(hash_value_object, T.nilable(Object))
|
|
340
|
-
raise TypeError, "Expected #{key} keys and values to be Symbols and Strings" unless hash_key.is_a?(Symbol) &&
|
|
341
|
-
hash_value.is_a?(String)
|
|
342
|
-
|
|
343
|
-
result[hash_key] = hash_value
|
|
344
|
-
end
|
|
345
|
-
result
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
sig { params(value: T.nilable(Object)).returns(T.nilable(T::Hash[Symbol, Object])) }
|
|
349
|
-
def symbol_object_hash(value)
|
|
350
|
-
return unless value.is_a?(Hash)
|
|
351
|
-
|
|
352
|
-
value
|
|
353
|
-
end
|
|
354
|
-
|
|
355
322
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
356
323
|
def buildfiles
|
|
357
324
|
@buildfiles ||= T.let(dependency_files.reject(&:support_file?), T.nilable(T::Array[Dependabot::DependencyFile]))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -66,11 +66,12 @@ module Dependabot
|
|
|
66
66
|
|
|
67
67
|
sig { override.returns(String) }
|
|
68
68
|
def maven_repo_url
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
requirement = dependency.requirements.find(&:source)
|
|
70
|
+
return Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL unless requirement
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
raise TypeError, "Gradle repository source must be a hash" if requirement.source_hash.nil?
|
|
73
|
+
|
|
74
|
+
requirement.source_string("url") ||
|
|
74
75
|
Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL
|
|
75
76
|
end
|
|
76
77
|
|
|
@@ -81,12 +82,12 @@ module Dependabot
|
|
|
81
82
|
|
|
82
83
|
sig { returns(T::Boolean) }
|
|
83
84
|
def plugin?
|
|
84
|
-
dependency.requirements.any? { |r| r.
|
|
85
|
+
dependency.requirements.any? { |r| r.groups&.include?("plugins") }
|
|
85
86
|
end
|
|
86
87
|
|
|
87
88
|
sig { returns(T::Boolean) }
|
|
88
89
|
def kotlin_plugin?
|
|
89
|
-
plugin? && dependency.requirements.any? { |r| r.
|
|
90
|
+
plugin? && dependency.requirements.any? { |r| r.groups&.include?("kotlin") }
|
|
90
91
|
end
|
|
91
92
|
end
|
|
92
93
|
end
|
|
@@ -86,7 +86,7 @@ module Dependabot
|
|
|
86
86
|
@dependency_files = dependency_files
|
|
87
87
|
@credentials = credentials
|
|
88
88
|
@forbidden_urls = forbidden_urls
|
|
89
|
-
@cooldown_options =
|
|
89
|
+
@cooldown_options = cooldown_options
|
|
90
90
|
@repositories = T.let(nil, T.nilable(T::Array[T::Hash[String, Object]]))
|
|
91
91
|
@google_version_details = T.let(nil, T.nilable(Nokogiri::XML::Document))
|
|
92
92
|
@dependency_repository_details = T.let(nil, T.nilable(T::Array[T::Hash[String, Object]]))
|
|
@@ -346,7 +346,7 @@ module Dependabot
|
|
|
346
346
|
def dependency_repository_details
|
|
347
347
|
requirement_files =
|
|
348
348
|
dependency.requirements
|
|
349
|
-
.
|
|
349
|
+
.filter_map(&:file)
|
|
350
350
|
.map { |nm| dependency_files.find { |f| f.name == nm } }
|
|
351
351
|
|
|
352
352
|
@dependency_repository_details ||=
|
|
@@ -396,7 +396,7 @@ module Dependabot
|
|
|
396
396
|
|
|
397
397
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
398
398
|
def pom
|
|
399
|
-
filename = T.must(dependency.requirements.first).
|
|
399
|
+
filename = T.must(dependency.requirements.first).file
|
|
400
400
|
dependency_files.find { |f| f.name == filename }
|
|
401
401
|
end
|
|
402
402
|
|
|
@@ -425,12 +425,12 @@ module Dependabot
|
|
|
425
425
|
|
|
426
426
|
sig { returns(T::Boolean) }
|
|
427
427
|
def plugin?
|
|
428
|
-
dependency.requirements.any? { |r| r.
|
|
428
|
+
dependency.requirements.any? { |r| r.groups&.include?("plugins") }
|
|
429
429
|
end
|
|
430
430
|
|
|
431
431
|
sig { returns(T.nilable(T::Boolean)) }
|
|
432
432
|
def kotlin_plugin?
|
|
433
|
-
plugin? && dependency.requirements.any? { |r| r.
|
|
433
|
+
plugin? && dependency.requirements.any? { |r| r.groups&.include?("kotlin") }
|
|
434
434
|
end
|
|
435
435
|
|
|
436
436
|
sig { returns(T::Boolean) }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -32,9 +32,9 @@ module Dependabot
|
|
|
32
32
|
ignored_versions:,
|
|
33
33
|
raise_on_ignored: false
|
|
34
34
|
)
|
|
35
|
-
@dependency =
|
|
36
|
-
@dependency_files =
|
|
37
|
-
@credentials =
|
|
35
|
+
@dependency = dependency
|
|
36
|
+
@dependency_files = dependency_files
|
|
37
|
+
@credentials = credentials
|
|
38
38
|
@target_version = T.let(
|
|
39
39
|
version_from_details(target_version_details),
|
|
40
40
|
T.nilable(Dependabot::Gradle::Version)
|
|
@@ -43,14 +43,14 @@ module Dependabot
|
|
|
43
43
|
source_url_from_details(target_version_details),
|
|
44
44
|
T.nilable(String)
|
|
45
45
|
)
|
|
46
|
-
@ignored_versions =
|
|
47
|
-
@raise_on_ignored =
|
|
46
|
+
@ignored_versions = ignored_versions
|
|
47
|
+
@raise_on_ignored = raise_on_ignored
|
|
48
48
|
@update_possible = T.let(nil, T.nilable(T::Boolean))
|
|
49
49
|
@updated_dependencies = T.let(nil, T.nilable(T::Array[Dependabot::Dependency]))
|
|
50
50
|
@dependencies_to_update = T.let(nil, T.nilable(T::Array[Dependabot::Dependency]))
|
|
51
51
|
@property_name = T.let(nil, T.nilable(String))
|
|
52
52
|
@dependency_set = T.let(nil, T.nilable(T::Hash[Symbol, String]))
|
|
53
|
-
@updated_requirements = T.let({}, T::Hash[String, T::Array[
|
|
53
|
+
@updated_requirements = T.let({}, T::Hash[String, T::Array[Dependabot::DependencyRequirement]])
|
|
54
54
|
end
|
|
55
55
|
sig { returns(T::Boolean) }
|
|
56
56
|
def update_possible?
|
|
@@ -128,8 +128,8 @@ module Dependabot
|
|
|
128
128
|
source: nil
|
|
129
129
|
).parse.select do |dep|
|
|
130
130
|
dep.requirements.any? do |r|
|
|
131
|
-
tmp_p_name = r.
|
|
132
|
-
tmp_dep_set = r.
|
|
131
|
+
tmp_p_name = r.metadata_string("property_name")
|
|
132
|
+
tmp_dep_set = r.metadata_string_hash("dependency_set")
|
|
133
133
|
next true if property_name && tmp_p_name == property_name
|
|
134
134
|
|
|
135
135
|
dependency_set && tmp_dep_set == dependency_set
|
|
@@ -140,18 +140,18 @@ module Dependabot
|
|
|
140
140
|
sig { returns(T.nilable(String)) }
|
|
141
141
|
def property_name
|
|
142
142
|
@property_name ||= dependency.requirements
|
|
143
|
-
.find { |r| r.
|
|
144
|
-
&.
|
|
143
|
+
.find { |r| r.metadata_string("property_name") }
|
|
144
|
+
&.metadata_string("property_name")
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
sig { returns(T.nilable(T::Hash[Symbol, String])) }
|
|
148
148
|
def dependency_set
|
|
149
149
|
@dependency_set ||= dependency.requirements
|
|
150
|
-
.find { |r| r.
|
|
151
|
-
&.
|
|
150
|
+
.find { |r| r.metadata_string_hash("dependency_set") }
|
|
151
|
+
&.metadata_string_hash("dependency_set")
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
-
sig { params(dep: Dependabot::Dependency).returns(T::Array[
|
|
154
|
+
sig { params(dep: Dependabot::Dependency).returns(T::Array[Dependabot::DependencyRequirement]) }
|
|
155
155
|
def updated_requirements(dep)
|
|
156
156
|
@updated_requirements[dep.name] ||=
|
|
157
157
|
RequirementsUpdater.new(
|
|
@@ -61,13 +61,14 @@ module Dependabot
|
|
|
61
61
|
# requirement at index `i` to correspond to the previous requirement
|
|
62
62
|
# at the same index.
|
|
63
63
|
requirements.map do |req|
|
|
64
|
-
|
|
65
|
-
next req if
|
|
64
|
+
requirement = req.requirement_string
|
|
65
|
+
next req if requirement.nil?
|
|
66
|
+
next req if requirement.include?(",")
|
|
66
67
|
|
|
67
|
-
property_name = req.
|
|
68
|
+
property_name = req.metadata_string("property_name")
|
|
68
69
|
next req if property_name && !properties_to_update.include?(property_name)
|
|
69
70
|
|
|
70
|
-
new_req = update_requirement(
|
|
71
|
+
new_req = update_requirement(requirement)
|
|
71
72
|
Dependabot::DependencyRequirement.create(req.merge(requirement: new_req, source: updated_source))
|
|
72
73
|
end
|
|
73
74
|
end
|
|
@@ -124,27 +125,29 @@ module Dependabot
|
|
|
124
125
|
distribution_url = T.let(nil, T.nilable(String))
|
|
125
126
|
|
|
126
127
|
requirements.map do |req|
|
|
127
|
-
source = req
|
|
128
|
+
source = req.source_hash
|
|
128
129
|
next req unless source
|
|
129
130
|
|
|
130
|
-
case
|
|
131
|
+
case req.source_string("property")
|
|
131
132
|
when "distributionUrl"
|
|
132
|
-
requirement = req
|
|
133
|
+
requirement = T.must(req.requirement_string)
|
|
133
134
|
version = update_exact_requirement(requirement)
|
|
134
|
-
distribution_url =
|
|
135
|
+
distribution_url = T.must(req.source_string("url")).gsub(requirement, version)
|
|
136
|
+
updated_source = set_source_string(source.dup, "url", distribution_url)
|
|
135
137
|
|
|
136
138
|
Dependabot::DependencyRequirement.create(
|
|
137
139
|
req.merge(
|
|
138
140
|
requirement: version,
|
|
139
|
-
source:
|
|
141
|
+
source: updated_source
|
|
140
142
|
)
|
|
141
143
|
)
|
|
142
144
|
when "distributionSha256Sum"
|
|
143
145
|
checksum_url, checksum = Gradle::Package::DistributionsFetcher.resolve_checksum(T.must(distribution_url))
|
|
146
|
+
updated_source = set_source_string(source.dup, "url", checksum_url)
|
|
144
147
|
Dependabot::DependencyRequirement.create(
|
|
145
148
|
req.merge(
|
|
146
149
|
requirement: checksum,
|
|
147
|
-
source:
|
|
150
|
+
source: updated_source
|
|
148
151
|
)
|
|
149
152
|
)
|
|
150
153
|
else
|
|
@@ -153,6 +156,23 @@ module Dependabot
|
|
|
153
156
|
end
|
|
154
157
|
end
|
|
155
158
|
|
|
159
|
+
sig do
|
|
160
|
+
params(
|
|
161
|
+
source: Dependabot::DependencyRequirement::ObjectHash,
|
|
162
|
+
key: String,
|
|
163
|
+
value: T.nilable(String)
|
|
164
|
+
).returns(Dependabot::DependencyRequirement::ObjectHash)
|
|
165
|
+
end
|
|
166
|
+
def set_source_string(source, key, value)
|
|
167
|
+
symbol_key = key.to_sym
|
|
168
|
+
if source.key?(key) && !source.key?(symbol_key)
|
|
169
|
+
source[key] = value
|
|
170
|
+
else
|
|
171
|
+
source[symbol_key] = value
|
|
172
|
+
end
|
|
173
|
+
source
|
|
174
|
+
end
|
|
175
|
+
|
|
156
176
|
sig { override.returns(T::Class[Version]) }
|
|
157
177
|
def version_class
|
|
158
178
|
Gradle::Version
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -206,34 +206,30 @@ module Dependabot
|
|
|
206
206
|
next false if dep.name == dependency.name
|
|
207
207
|
|
|
208
208
|
dep.requirements.any? do |req|
|
|
209
|
-
req.
|
|
209
|
+
req.metadata_string("property_name") == property_name
|
|
210
210
|
end
|
|
211
211
|
end
|
|
212
212
|
end
|
|
213
213
|
end
|
|
214
214
|
|
|
215
|
-
sig { params(requirement:
|
|
215
|
+
sig { params(requirement: Dependabot::DependencyRequirement).returns(T.nilable(String)) }
|
|
216
216
|
def property_name_from_requirement(requirement)
|
|
217
|
-
|
|
218
|
-
return unless metadata.is_a?(Hash)
|
|
219
|
-
|
|
220
|
-
property_name = metadata[:property_name]
|
|
221
|
-
property_name if property_name.is_a?(String)
|
|
217
|
+
requirement.metadata_string("property_name")
|
|
222
218
|
end
|
|
223
219
|
|
|
224
220
|
sig { returns(T::Boolean) }
|
|
225
221
|
def version_comes_from_dependency_set?
|
|
226
222
|
dependency.requirements.any? do |req|
|
|
227
|
-
req.
|
|
223
|
+
req.metadata_string_hash("dependency_set")
|
|
228
224
|
end
|
|
229
225
|
end
|
|
230
226
|
|
|
231
|
-
sig { returns(T::Array[
|
|
227
|
+
sig { returns(T::Array[Dependabot::DependencyRequirement]) }
|
|
232
228
|
def declarations_using_a_property
|
|
233
229
|
@declarations_using_a_property ||= T.let(
|
|
234
230
|
dependency.requirements
|
|
235
|
-
.select { |req| req.
|
|
236
|
-
T.nilable(T::Array[
|
|
231
|
+
.select { |req| req.metadata_string("property_name") },
|
|
232
|
+
T.nilable(T::Array[Dependabot::DependencyRequirement])
|
|
237
233
|
)
|
|
238
234
|
end
|
|
239
235
|
|
|
@@ -244,7 +240,7 @@ module Dependabot
|
|
|
244
240
|
dependency_files: dependency_files,
|
|
245
241
|
source: nil
|
|
246
242
|
).parse.select do |dep|
|
|
247
|
-
dep.requirements.any? { |req| req.
|
|
243
|
+
dep.requirements.any? { |req| req.metadata_string("property_name") }
|
|
248
244
|
end,
|
|
249
245
|
T.nilable(T::Array[Dependabot::Dependency])
|
|
250
246
|
)
|
|
@@ -15,7 +15,7 @@ module Dependabot
|
|
|
15
15
|
class Version < Dependabot::Version
|
|
16
16
|
extend T::Sig
|
|
17
17
|
|
|
18
|
-
NULL_VALUES =
|
|
18
|
+
NULL_VALUES = %w(0 final ga).freeze
|
|
19
19
|
PREFIXED_TOKEN_HIERARCHY = T.let(
|
|
20
20
|
{
|
|
21
21
|
"." => { qualifier: 1, number: 4 },
|
|
@@ -39,13 +39,10 @@ module Dependabot
|
|
|
39
39
|
}.freeze,
|
|
40
40
|
T::Hash[String, Integer]
|
|
41
41
|
)
|
|
42
|
-
VERSION_PATTERN =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
String
|
|
47
|
-
)
|
|
48
|
-
ANCHORED_VERSION_PATTERN = T.let(/\A\s*(#{VERSION_PATTERN})?\s*\z/, Regexp)
|
|
42
|
+
VERSION_PATTERN = "[0-9a-zA-Z]+" \
|
|
43
|
+
'(?>\.[0-9a-zA-Z]*)*' \
|
|
44
|
+
'([_\-\+][0-9A-Za-z_-]*(\.[0-9A-Za-z_-]*)*)?'
|
|
45
|
+
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})?\s*\z/
|
|
49
46
|
|
|
50
47
|
sig { override.params(version: Object).returns(T::Boolean) }
|
|
51
48
|
def self.correct?(version)
|
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.391.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.391.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.391.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.391.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.391.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: debug
|
|
42
42
|
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.391.0
|
|
295
295
|
rdoc_options: []
|
|
296
296
|
require_paths:
|
|
297
297
|
- lib
|