dependabot-sbt 0.377.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 +7 -0
- data/lib/dependabot/sbt/file_fetcher.rb +121 -0
- data/lib/dependabot/sbt/file_parser/property_value_finder.rb +179 -0
- data/lib/dependabot/sbt/file_parser/repositories_finder.rb +141 -0
- data/lib/dependabot/sbt/file_parser.rb +468 -0
- data/lib/dependabot/sbt/file_updater/property_value_updater.rb +28 -0
- data/lib/dependabot/sbt/file_updater.rb +240 -0
- data/lib/dependabot/sbt/language.rb +24 -0
- data/lib/dependabot/sbt/metadata_finder.rb +24 -0
- data/lib/dependabot/sbt/native_helpers.rb +28 -0
- data/lib/dependabot/sbt/package/package_details_fetcher.rb +175 -0
- data/lib/dependabot/sbt/package_manager.rb +39 -0
- data/lib/dependabot/sbt/requirement.rb +64 -0
- data/lib/dependabot/sbt/update_checker/requirements_updater.rb +98 -0
- data/lib/dependabot/sbt/update_checker/version_finder.rb +76 -0
- data/lib/dependabot/sbt/update_checker.rb +177 -0
- data/lib/dependabot/sbt/version.rb +21 -0
- data/lib/dependabot/sbt.rb +19 -0
- metadata +298 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
require "dependabot/file_updaters"
|
|
7
|
+
require "dependabot/file_updaters/base"
|
|
8
|
+
require "dependabot/sbt/file_parser"
|
|
9
|
+
|
|
10
|
+
module Dependabot
|
|
11
|
+
module Sbt
|
|
12
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
13
|
+
extend T::Sig
|
|
14
|
+
|
|
15
|
+
require_relative "file_updater/property_value_updater"
|
|
16
|
+
|
|
17
|
+
# Regex matching scalaVersion declarations in all supported SBT syntaxes
|
|
18
|
+
SCALA_VERSION_DECL = T.let(
|
|
19
|
+
"(?:ThisBuild\\s*/\\s*)?" \
|
|
20
|
+
"(?:scalaVersion\\s+in\\s+ThisBuild|scalaVersion)" \
|
|
21
|
+
'\\s*:=\\s*"',
|
|
22
|
+
String
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
|
26
|
+
def updated_dependency_files
|
|
27
|
+
updated_files = T.let(dependency_files.dup, T::Array[Dependabot::DependencyFile])
|
|
28
|
+
|
|
29
|
+
dependencies.each do |dependency|
|
|
30
|
+
updated_files = update_files_for_dependency(
|
|
31
|
+
original_files: updated_files,
|
|
32
|
+
dependency: dependency
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
updated_files.reject! { |f| dependency_files.include?(f) }
|
|
37
|
+
|
|
38
|
+
raise "No files changed!" if updated_files.none?
|
|
39
|
+
|
|
40
|
+
updated_files
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
sig { override.void }
|
|
46
|
+
def check_required_files
|
|
47
|
+
raise "No build.sbt!" unless get_original_file("build.sbt")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
sig do
|
|
51
|
+
params(
|
|
52
|
+
original_files: T::Array[Dependabot::DependencyFile],
|
|
53
|
+
dependency: Dependabot::Dependency
|
|
54
|
+
).returns(T::Array[Dependabot::DependencyFile])
|
|
55
|
+
end
|
|
56
|
+
def update_files_for_dependency(original_files:, dependency:)
|
|
57
|
+
files = original_files.dup
|
|
58
|
+
|
|
59
|
+
reqs = dependency.requirements.zip(dependency.previous_requirements.to_a)
|
|
60
|
+
.reject { |new_req, old_req| new_req == old_req }
|
|
61
|
+
|
|
62
|
+
reqs.each do |new_req, old_req|
|
|
63
|
+
raise "Bad req match" unless new_req[:file] == T.must(old_req)[:file]
|
|
64
|
+
next if new_req[:requirement] == T.must(old_req)[:requirement]
|
|
65
|
+
|
|
66
|
+
files = apply_requirement_update(files, dependency, new_req, T.must(old_req))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
files
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
sig do
|
|
73
|
+
params(
|
|
74
|
+
files: T::Array[Dependabot::DependencyFile],
|
|
75
|
+
dependency: Dependabot::Dependency,
|
|
76
|
+
new_req: T::Hash[Symbol, T.untyped],
|
|
77
|
+
old_req: T::Hash[Symbol, T.untyped]
|
|
78
|
+
).returns(T::Array[Dependabot::DependencyFile])
|
|
79
|
+
end
|
|
80
|
+
def apply_requirement_update(files, dependency, new_req, old_req)
|
|
81
|
+
if new_req.dig(:metadata, :property_name)
|
|
82
|
+
update_files_for_property_change(files, old_req, new_req)
|
|
83
|
+
elsif T.let(new_req[:file], String).end_with?("build.properties")
|
|
84
|
+
update_build_properties(files, old_req, new_req)
|
|
85
|
+
elsif scala_version_requirement?(new_req)
|
|
86
|
+
file = T.must(files.find { |f| f.name == new_req[:file] })
|
|
87
|
+
idx = T.must(files.index(file))
|
|
88
|
+
files.dup.tap { |updated| updated[idx] = update_scala_version(file, old_req, new_req) }
|
|
89
|
+
else
|
|
90
|
+
file = T.must(files.find { |f| f.name == new_req[:file] })
|
|
91
|
+
idx = T.must(files.index(file))
|
|
92
|
+
files.dup.tap { |updated| updated[idx] = update_version_in_buildfile(dependency, file, old_req, new_req) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
sig do
|
|
97
|
+
params(
|
|
98
|
+
files: T::Array[Dependabot::DependencyFile],
|
|
99
|
+
old_req: T::Hash[Symbol, T.untyped],
|
|
100
|
+
new_req: T::Hash[Symbol, T.untyped]
|
|
101
|
+
).returns(T::Array[Dependabot::DependencyFile])
|
|
102
|
+
end
|
|
103
|
+
def update_files_for_property_change(files, old_req, new_req)
|
|
104
|
+
property_name = T.let(new_req.dig(:metadata, :property_name), String)
|
|
105
|
+
callsite = T.must(files.find { |f| f.name == new_req[:file] })
|
|
106
|
+
|
|
107
|
+
PropertyValueUpdater.new(dependency_files: files)
|
|
108
|
+
.update_files_for_property_change(
|
|
109
|
+
property_name: property_name,
|
|
110
|
+
callsite_buildfile: callsite,
|
|
111
|
+
previous_value: T.let(old_req.fetch(:requirement), String),
|
|
112
|
+
updated_value: T.let(new_req.fetch(:requirement), String)
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
sig do
|
|
117
|
+
params(
|
|
118
|
+
files: T::Array[Dependabot::DependencyFile],
|
|
119
|
+
old_req: T::Hash[Symbol, T.untyped],
|
|
120
|
+
new_req: T::Hash[Symbol, T.untyped]
|
|
121
|
+
).returns(T::Array[Dependabot::DependencyFile])
|
|
122
|
+
end
|
|
123
|
+
def update_build_properties(files, old_req, new_req)
|
|
124
|
+
file = T.must(files.find { |f| f.name == new_req[:file] })
|
|
125
|
+
old_version = T.let(old_req.fetch(:requirement), String)
|
|
126
|
+
new_version = T.let(new_req.fetch(:requirement), String)
|
|
127
|
+
|
|
128
|
+
updated_content = T.must(file.content).sub(
|
|
129
|
+
/(sbt\.version\s*=\s*)#{Regexp.quote(old_version)}/,
|
|
130
|
+
"\\1#{new_version}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
raise "Expected content to change!" if updated_content == file.content
|
|
134
|
+
|
|
135
|
+
updated_files = files.dup
|
|
136
|
+
updated_files[T.must(files.index(file))] =
|
|
137
|
+
updated_file(file: file, content: updated_content)
|
|
138
|
+
updated_files
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
sig do
|
|
142
|
+
params(
|
|
143
|
+
file: Dependabot::DependencyFile,
|
|
144
|
+
old_req: T::Hash[Symbol, T.untyped],
|
|
145
|
+
new_req: T::Hash[Symbol, T.untyped]
|
|
146
|
+
).returns(Dependabot::DependencyFile)
|
|
147
|
+
end
|
|
148
|
+
def update_scala_version(file, old_req, new_req)
|
|
149
|
+
old_version = T.let(old_req.fetch(:requirement), String)
|
|
150
|
+
new_version = T.let(new_req.fetch(:requirement), String)
|
|
151
|
+
|
|
152
|
+
updated_content = T.must(file.content).sub(
|
|
153
|
+
/(#{SCALA_VERSION_DECL})#{Regexp.quote(old_version)}(")/,
|
|
154
|
+
"\\1#{new_version}\\2"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
raise "Expected content to change!" if updated_content == file.content
|
|
158
|
+
|
|
159
|
+
updated_file(file: file, content: updated_content)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
sig do
|
|
163
|
+
params(
|
|
164
|
+
dependency: Dependabot::Dependency,
|
|
165
|
+
buildfile: Dependabot::DependencyFile,
|
|
166
|
+
previous_req: T::Hash[Symbol, T.untyped],
|
|
167
|
+
requirement: T::Hash[Symbol, T.untyped]
|
|
168
|
+
).returns(Dependabot::DependencyFile)
|
|
169
|
+
end
|
|
170
|
+
def update_version_in_buildfile(dependency, buildfile, previous_req, requirement)
|
|
171
|
+
updated_content = T.must(buildfile.content.dup)
|
|
172
|
+
|
|
173
|
+
original_declarations = original_buildfile_declarations(dependency, previous_req)
|
|
174
|
+
original_declarations.each do |old_dec|
|
|
175
|
+
updated_content = updated_content.gsub(old_dec) do
|
|
176
|
+
updated_buildfile_declaration(old_dec, previous_req, requirement)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
raise "Expected content to change!" if updated_content == buildfile.content
|
|
181
|
+
|
|
182
|
+
updated_file(file: buildfile, content: updated_content)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
sig do
|
|
186
|
+
params(
|
|
187
|
+
dependency: Dependabot::Dependency,
|
|
188
|
+
requirement: T::Hash[Symbol, T.untyped]
|
|
189
|
+
).returns(T::Array[String])
|
|
190
|
+
end
|
|
191
|
+
def original_buildfile_declarations(dependency, requirement)
|
|
192
|
+
buildfile = T.must(dependency_files.find { |f| f.name == T.let(requirement.fetch(:file), String) })
|
|
193
|
+
group, artifact = dependency_group_and_artifact(dependency)
|
|
194
|
+
|
|
195
|
+
T.must(buildfile.content).lines.select do |line|
|
|
196
|
+
next false unless line.include?(group)
|
|
197
|
+
next false unless line.include?(artifact)
|
|
198
|
+
|
|
199
|
+
line.include?(T.let(requirement.fetch(:requirement), String))
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
sig { params(dependency: Dependabot::Dependency).returns([String, String]) }
|
|
204
|
+
def dependency_group_and_artifact(dependency)
|
|
205
|
+
parts = dependency.name.split(":")
|
|
206
|
+
group = T.must(parts.first)
|
|
207
|
+
artifact = T.must(parts.last)
|
|
208
|
+
|
|
209
|
+
# Strip Scala version suffix for cross-versioned deps (e.g. cats-core_2.13 → cats-core)
|
|
210
|
+
artifact = artifact.sub(/_\d+(\.\d+)?$/, "")
|
|
211
|
+
|
|
212
|
+
[group, artifact]
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
sig do
|
|
216
|
+
params(
|
|
217
|
+
old_declaration: String,
|
|
218
|
+
previous_req: T::Hash[Symbol, T.untyped],
|
|
219
|
+
requirement: T::Hash[Symbol, T.untyped]
|
|
220
|
+
).returns(String)
|
|
221
|
+
end
|
|
222
|
+
def updated_buildfile_declaration(old_declaration, previous_req, requirement)
|
|
223
|
+
original_req_string = T.let(previous_req.fetch(:requirement), String)
|
|
224
|
+
new_req_string = T.let(requirement.fetch(:requirement), String)
|
|
225
|
+
|
|
226
|
+
old_declaration.gsub(
|
|
227
|
+
/"#{Regexp.quote(original_req_string)}"/,
|
|
228
|
+
"\"#{new_req_string}\""
|
|
229
|
+
)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
sig { params(req: T::Hash[Symbol, T.untyped]).returns(T::Boolean) }
|
|
233
|
+
def scala_version_requirement?(req)
|
|
234
|
+
req.dig(:metadata, :property_source) == "scalaVersion"
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
Dependabot::FileUpdaters.register("sbt", Dependabot::Sbt::FileUpdater)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/ecosystem"
|
|
6
|
+
require "dependabot/sbt/version"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Sbt
|
|
10
|
+
LANGUAGE = "scala"
|
|
11
|
+
|
|
12
|
+
class Language < Dependabot::Ecosystem::VersionManager
|
|
13
|
+
extend T::Sig
|
|
14
|
+
|
|
15
|
+
sig { params(raw_version: String).void }
|
|
16
|
+
def initialize(raw_version)
|
|
17
|
+
super(
|
|
18
|
+
name: LANGUAGE,
|
|
19
|
+
version: Version.new(raw_version)
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/metadata_finders"
|
|
5
|
+
require "dependabot/maven/shared/shared_metadata_finder"
|
|
6
|
+
require "dependabot/sbt/file_fetcher"
|
|
7
|
+
require "sorbet-runtime"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Sbt
|
|
11
|
+
class MetadataFinder < Dependabot::Maven::Shared::SharedMetadataFinder
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
sig { override.returns(T.class_of(Dependabot::FileFetchers::Base)) }
|
|
17
|
+
def file_fetcher_class
|
|
18
|
+
Dependabot::Sbt::FileFetcher
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Dependabot::MetadataFinders.register("sbt", Dependabot::Sbt::MetadataFinder)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module Sbt
|
|
8
|
+
module NativeHelpers
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
sig { returns(String) }
|
|
12
|
+
def self.coursier_path
|
|
13
|
+
clean_path(File.join(native_helpers_root, "sbt/bin/cs"))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
sig { returns(String) }
|
|
17
|
+
def self.native_helpers_root
|
|
18
|
+
default_path = File.join(__dir__, "../../../helpers/install-dir")
|
|
19
|
+
ENV.fetch("DEPENDABOT_NATIVE_HELPERS_PATH", default_path)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
sig { params(path: String).returns(String) }
|
|
23
|
+
def self.clean_path(path)
|
|
24
|
+
Pathname.new(path).cleanpath.to_path
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "nokogiri"
|
|
5
|
+
require "sorbet-runtime"
|
|
6
|
+
require "dependabot/registry_client"
|
|
7
|
+
require "dependabot/package/package_release"
|
|
8
|
+
require "dependabot/package/package_details"
|
|
9
|
+
require "dependabot/sbt/file_parser/repositories_finder"
|
|
10
|
+
require "dependabot/sbt/version"
|
|
11
|
+
require "dependabot/sbt/requirement"
|
|
12
|
+
require "dependabot/maven/shared/shared_package_details_fetcher"
|
|
13
|
+
require "dependabot/maven/utils/auth_headers_finder"
|
|
14
|
+
|
|
15
|
+
module Dependabot
|
|
16
|
+
module Sbt
|
|
17
|
+
module Package
|
|
18
|
+
class PackageDetailsFetcher < Dependabot::Maven::Shared::SharedPackageDetailsFetcher
|
|
19
|
+
extend T::Sig
|
|
20
|
+
|
|
21
|
+
sig do
|
|
22
|
+
params(
|
|
23
|
+
dependency: Dependabot::Dependency,
|
|
24
|
+
dependency_files: T::Array[Dependabot::DependencyFile],
|
|
25
|
+
credentials: T::Array[Dependabot::Credential]
|
|
26
|
+
).void
|
|
27
|
+
end
|
|
28
|
+
def initialize(dependency:, dependency_files:, credentials:)
|
|
29
|
+
@dependency = T.let(dependency, Dependabot::Dependency)
|
|
30
|
+
@dependency_files = T.let(dependency_files, T::Array[Dependabot::DependencyFile])
|
|
31
|
+
@credentials = T.let(credentials, T::Array[Dependabot::Credential])
|
|
32
|
+
|
|
33
|
+
@repositories_cache = T.let(nil, T.nilable(T::Array[T::Hash[String, T.untyped]]))
|
|
34
|
+
@repository_finder = T.let(nil, T.nilable(Sbt::FileParser::RepositoriesFinder))
|
|
35
|
+
@package_details = T.let(nil, T.nilable(Dependabot::Package::PackageDetails))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
sig { override.returns(Dependabot::Dependency) }
|
|
39
|
+
attr_reader :dependency
|
|
40
|
+
|
|
41
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
42
|
+
attr_reader :dependency_files
|
|
43
|
+
|
|
44
|
+
sig { override.returns(T::Array[Dependabot::Credential]) }
|
|
45
|
+
attr_reader :credentials
|
|
46
|
+
|
|
47
|
+
sig { returns(Dependabot::Package::PackageDetails) }
|
|
48
|
+
def fetch
|
|
49
|
+
return @package_details if @package_details
|
|
50
|
+
|
|
51
|
+
releases = versions.map do |version_details|
|
|
52
|
+
Dependabot::Package::PackageRelease.new(
|
|
53
|
+
version: version_details.fetch(:version),
|
|
54
|
+
released_at: version_details.fetch(:release_date, nil),
|
|
55
|
+
url: version_details.fetch(:source_url)
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@package_details = Dependabot::Package::PackageDetails.new(
|
|
60
|
+
dependency: dependency,
|
|
61
|
+
releases: releases
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
@package_details
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sig { returns(T::Array[Dependabot::Package::PackageRelease]) }
|
|
68
|
+
def releases
|
|
69
|
+
fetch.releases
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Assembles the list of Maven repositories to search: credential repos + SBT resolver repos.
|
|
73
|
+
sig { override.returns(T::Array[T::Hash[String, T.untyped]]) }
|
|
74
|
+
def repositories
|
|
75
|
+
return @repositories_cache if @repositories_cache
|
|
76
|
+
|
|
77
|
+
@repositories_cache = credentials_repository_details
|
|
78
|
+
|
|
79
|
+
sbt_repository_details.each do |repo|
|
|
80
|
+
@repositories_cache << repo unless @repositories_cache.any? do |r|
|
|
81
|
+
r[URL_KEY] == repo[URL_KEY]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
@repositories_cache
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
sig { override.returns(String) }
|
|
89
|
+
def central_repo_url
|
|
90
|
+
Sbt::FileParser::RepositoriesFinder::CENTRAL_REPO_URL
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Override to always use "jar" for the HEAD check. The SBT parser sets
|
|
94
|
+
# packaging_type to "cross-versioned" as metadata for %% dependencies, but the
|
|
95
|
+
# actual Maven artifact is always a .jar file.
|
|
96
|
+
sig { override.params(repository_url: String, version: Dependabot::Version).returns(String) }
|
|
97
|
+
def dependency_files_url(repository_url, version)
|
|
98
|
+
_, artifact_id = dependency_parts
|
|
99
|
+
base_url = dependency_base_url(repository_url)
|
|
100
|
+
|
|
101
|
+
"#{base_url}/#{version}/#{artifact_id}-#{version}.jar"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Override to handle SBT plugin cross-versioning.
|
|
105
|
+
# SBT plugins are published with a double-suffix: artifact_scalaVersion_sbtVersion
|
|
106
|
+
# e.g., sbt-jmh_2.12_1.0 for SBT 1.x plugins.
|
|
107
|
+
sig { override.returns([String, String]) }
|
|
108
|
+
def dependency_parts
|
|
109
|
+
@dependency_parts = T.let(@dependency_parts, T.nilable([String, String]))
|
|
110
|
+
return @dependency_parts if @dependency_parts
|
|
111
|
+
|
|
112
|
+
group_id, artifact_id = dependency.name.split(":")
|
|
113
|
+
group_path = T.must(group_id).tr(".", "/")
|
|
114
|
+
|
|
115
|
+
artifact_id = "#{artifact_id}_#{plugin_scala_version}_#{sbt_binary_version}" if sbt_plugin?
|
|
116
|
+
|
|
117
|
+
@dependency_parts = [group_path, T.must(artifact_id)]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
sig { returns(Sbt::FileParser::RepositoriesFinder) }
|
|
123
|
+
def repository_finder
|
|
124
|
+
@repository_finder ||= Sbt::FileParser::RepositoriesFinder.new(
|
|
125
|
+
dependency_files: dependency_files,
|
|
126
|
+
credentials: credentials
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Returns the repository details discovered from SBT build files.
|
|
131
|
+
sig { returns(T::Array[T::Hash[String, T.untyped]]) }
|
|
132
|
+
def sbt_repository_details
|
|
133
|
+
repository_finder
|
|
134
|
+
.repository_urls
|
|
135
|
+
.map do |url|
|
|
136
|
+
{ URL_KEY => url, AUTH_HEADERS_KEY => auth_headers(url) }
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# SBT plugins are identified by having "plugins" in their groups.
|
|
141
|
+
sig { returns(T::Boolean) }
|
|
142
|
+
def sbt_plugin?
|
|
143
|
+
dependency.requirements.any? { |req| req.fetch(:groups, []).include?("plugins") }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# SBT 1.x plugins use Scala 2.12; SBT 2.x plugins use Scala 3.
|
|
147
|
+
sig { returns(String) }
|
|
148
|
+
def plugin_scala_version
|
|
149
|
+
sbt_major_version >= 2 ? "3" : "2.12"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# SBT binary version for plugin cross-versioning: "1.0" for SBT 1.x, "2.0" for SBT 2.x.
|
|
153
|
+
sig { returns(String) }
|
|
154
|
+
def sbt_binary_version
|
|
155
|
+
"#{sbt_major_version}.0"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
sig { returns(Integer) }
|
|
159
|
+
def sbt_major_version
|
|
160
|
+
build_properties = dependency_files.find { |f| f.name.end_with?("build.properties") }
|
|
161
|
+
return 1 unless build_properties&.content
|
|
162
|
+
|
|
163
|
+
T.must(build_properties.content).each_line do |line|
|
|
164
|
+
match = line.strip.match(Sbt::FileParser::SBT_VERSION_REGEX)
|
|
165
|
+
next unless match
|
|
166
|
+
|
|
167
|
+
return T.must(match[:version]).strip.split(".").first.to_i
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
1
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/ecosystem"
|
|
6
|
+
require "dependabot/sbt/version"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Sbt
|
|
10
|
+
ECOSYSTEM = "sbt"
|
|
11
|
+
PACKAGE_MANAGER = "sbt"
|
|
12
|
+
SUPPORTED_SBT_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
|
13
|
+
DEPRECATED_SBT_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
|
14
|
+
|
|
15
|
+
class PackageManager < Dependabot::Ecosystem::VersionManager
|
|
16
|
+
extend T::Sig
|
|
17
|
+
|
|
18
|
+
sig { params(raw_version: String).void }
|
|
19
|
+
def initialize(raw_version)
|
|
20
|
+
super(
|
|
21
|
+
name: PACKAGE_MANAGER,
|
|
22
|
+
version: Version.new(raw_version),
|
|
23
|
+
deprecated_versions: DEPRECATED_SBT_VERSIONS,
|
|
24
|
+
supported_versions: SUPPORTED_SBT_VERSIONS
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
sig { returns(T::Boolean) }
|
|
29
|
+
def deprecated?
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
sig { returns(T::Boolean) }
|
|
34
|
+
def unsupported?
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
require "dependabot/requirement"
|
|
7
|
+
require "dependabot/utils"
|
|
8
|
+
require "dependabot/maven/shared/shared_requirement"
|
|
9
|
+
require "dependabot/sbt/version"
|
|
10
|
+
|
|
11
|
+
module Dependabot
|
|
12
|
+
module Sbt
|
|
13
|
+
# SBT typically uses exact versions ("org" % "artifact" % "1.2.3"),
|
|
14
|
+
# but Maven-style version ranges are valid since artifacts resolve
|
|
15
|
+
# from Maven repositories.
|
|
16
|
+
class Requirement < Dependabot::Maven::Shared::SharedRequirement
|
|
17
|
+
extend T::Sig
|
|
18
|
+
|
|
19
|
+
quoted = OPS.keys.map { |k| Regexp.quote k }.join("|")
|
|
20
|
+
PATTERN_RAW = T.let("\\s*(#{quoted})?\\s*(#{Sbt::Version::VERSION_PATTERN})\\s*".freeze, String)
|
|
21
|
+
PATTERN = T.let(/\A#{PATTERN_RAW}\z/, Regexp)
|
|
22
|
+
RUBY_STYLE_PATTERN = T.let(/\A\s*(#{quoted})\s*(#{Sbt::Version::VERSION_PATTERN})\s*\z/, Regexp)
|
|
23
|
+
|
|
24
|
+
sig { override.returns(Regexp) }
|
|
25
|
+
def self.pattern
|
|
26
|
+
PATTERN
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
sig { override.returns(Regexp) }
|
|
30
|
+
def self.ruby_style_pattern
|
|
31
|
+
RUBY_STYLE_PATTERN
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sig { override.params(obj: T.any(Gem::Version, String)).returns([String, Gem::Version]) }
|
|
35
|
+
def self.parse(obj)
|
|
36
|
+
return ["=", Sbt::Version.new(obj.to_s)] if obj.is_a?(Gem::Version)
|
|
37
|
+
|
|
38
|
+
unless (matches = PATTERN.match(obj.to_s))
|
|
39
|
+
msg = "Illformed requirement [#{obj.inspect}]"
|
|
40
|
+
raise BadRequirementError, msg
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
return DefaultRequirement if matches[1] == ">=" && matches[2] == "0"
|
|
44
|
+
|
|
45
|
+
[matches[1] || "=", Sbt::Version.new(T.must(matches[2]))]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
|
|
49
|
+
def self.requirements_array(requirement_string)
|
|
50
|
+
split_java_requirement(requirement_string).map do |str|
|
|
51
|
+
new(str)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
sig { override.params(version: Gem::Version).returns(T::Boolean) }
|
|
56
|
+
def satisfied_by?(version)
|
|
57
|
+
version = Sbt::Version.new(version.to_s)
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
Dependabot::Utils.register_requirement_class("sbt", Dependabot::Sbt::Requirement)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/requirements_updater/base"
|
|
6
|
+
require "dependabot/sbt/update_checker"
|
|
7
|
+
require "dependabot/sbt/version"
|
|
8
|
+
require "dependabot/sbt/requirement"
|
|
9
|
+
|
|
10
|
+
module Dependabot
|
|
11
|
+
module Sbt
|
|
12
|
+
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
13
|
+
class RequirementsUpdater
|
|
14
|
+
extend T::Sig
|
|
15
|
+
extend T::Generic
|
|
16
|
+
|
|
17
|
+
Version = type_member { { fixed: Dependabot::Sbt::Version } }
|
|
18
|
+
Requirement = type_member { { fixed: Dependabot::Sbt::Requirement } }
|
|
19
|
+
|
|
20
|
+
include Dependabot::RequirementsUpdater::Base
|
|
21
|
+
|
|
22
|
+
sig do
|
|
23
|
+
params(
|
|
24
|
+
requirements: T::Array[T::Hash[Symbol, T.untyped]],
|
|
25
|
+
latest_version: T.nilable(T.any(Version, String)),
|
|
26
|
+
source_url: T.nilable(String),
|
|
27
|
+
properties_to_update: T::Array[String]
|
|
28
|
+
).void
|
|
29
|
+
end
|
|
30
|
+
def initialize(
|
|
31
|
+
requirements:,
|
|
32
|
+
latest_version:,
|
|
33
|
+
source_url:,
|
|
34
|
+
properties_to_update:
|
|
35
|
+
)
|
|
36
|
+
@requirements = requirements
|
|
37
|
+
@source_url = source_url
|
|
38
|
+
@properties_to_update = properties_to_update
|
|
39
|
+
return unless latest_version
|
|
40
|
+
|
|
41
|
+
@latest_version = T.let(version_class.new(latest_version), Version)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
sig { override.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
45
|
+
def updated_requirements
|
|
46
|
+
return requirements unless latest_version
|
|
47
|
+
|
|
48
|
+
requirements.map do |req|
|
|
49
|
+
next req if req.fetch(:requirement).nil?
|
|
50
|
+
next req if req.fetch(:requirement).include?(",")
|
|
51
|
+
|
|
52
|
+
property_name = req.dig(:metadata, :property_name)
|
|
53
|
+
next req if property_name && !properties_to_update.include?(property_name)
|
|
54
|
+
|
|
55
|
+
new_req = update_requirement(req[:requirement])
|
|
56
|
+
req.merge(requirement: new_req, source: updated_source)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
|
63
|
+
attr_reader :requirements
|
|
64
|
+
|
|
65
|
+
sig { returns(T.nilable(Version)) }
|
|
66
|
+
attr_reader :latest_version
|
|
67
|
+
|
|
68
|
+
sig { returns(T.nilable(String)) }
|
|
69
|
+
attr_reader :source_url
|
|
70
|
+
|
|
71
|
+
sig { returns(T::Array[String]) }
|
|
72
|
+
attr_reader :properties_to_update
|
|
73
|
+
|
|
74
|
+
sig { params(req_string: String).returns(String) }
|
|
75
|
+
def update_requirement(req_string)
|
|
76
|
+
old_version = requirement_class.new(req_string)
|
|
77
|
+
.requirements.first.last
|
|
78
|
+
req_string.gsub(old_version.to_s, T.must(latest_version).to_s)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
sig { override.returns(T::Class[Version]) }
|
|
82
|
+
def version_class
|
|
83
|
+
Sbt::Version
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
sig { override.returns(T::Class[Requirement]) }
|
|
87
|
+
def requirement_class
|
|
88
|
+
Sbt::Requirement
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
92
|
+
def updated_source
|
|
93
|
+
{ type: "maven_repo", url: source_url }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|