dependabot-maven 0.391.0 → 0.392.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/maven/distributions.rb +31 -0
- data/lib/dependabot/maven/file_fetcher.rb +72 -1
- data/lib/dependabot/maven/file_parser/wrapper_mojo.rb +407 -0
- data/lib/dependabot/maven/file_parser.rb +122 -32
- data/lib/dependabot/maven/file_updater/dependency_requirement_scopes.rb +90 -0
- data/lib/dependabot/maven/file_updater/wrapper_updater.rb +500 -0
- data/lib/dependabot/maven/file_updater.rb +79 -6
- data/lib/dependabot/maven/native_helpers.rb +48 -1
- data/lib/dependabot/maven/update_checker/requirements_updater.rb +67 -1
- metadata +8 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d7384e20ca998f4af49001eb34de080505f3abff092e4faa921424ad6b65299e
|
|
4
|
+
data.tar.gz: 3c7f12c0289d6a8e811d0d51c6d24af81a319e59ba722fe773d623433ddb6ab9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8da50ec13cdb3fead27bd2e0c20e019650a8f38a9817be6fb17f8ec5ceef8b7ffb19b7e6805a7f40787de1c4d029d7692e35149a1fc10901d2f48d043c4a02b
|
|
7
|
+
data.tar.gz: f6f258473e1bff627f213be788e692c6ddfaced18172fdc6910307f504c12554ed88b5447163932e4d909b9be856aaa5921669e1f572de0e81c3ff6d865781a5
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "dependabot/dependency_requirement"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module Maven
|
|
8
|
+
module Distributions
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
# Used to distinguish wrapper requirements (which live in maven-wrapper.properties)
|
|
12
|
+
# from regular POM requirements (which live in pom.xml)
|
|
13
|
+
DISTRIBUTION_DEPENDENCY_TYPE = "maven-distribution"
|
|
14
|
+
|
|
15
|
+
# Maven and the maven-wrapper plugin release independently with separate cadences.
|
|
16
|
+
# Tracking them as distinct dependencies allows users to update each on their own
|
|
17
|
+
# schedule. Users who prefer batched updates can use grouped updates.
|
|
18
|
+
|
|
19
|
+
MAVEN_DISTRIBUTION_PACKAGE = "org.apache.maven:apache-maven"
|
|
20
|
+
MAVEN_WRAPPER_PACKAGE = "org.apache.maven.wrapper:maven-wrapper"
|
|
21
|
+
|
|
22
|
+
sig { params(requirements: T::Array[Dependabot::DependencyRequirement]).returns(T::Boolean) }
|
|
23
|
+
def self.distribution_requirements?(requirements)
|
|
24
|
+
# Returns true if any requirement came from a maven-wrapper.properties
|
|
25
|
+
# file rather than a pom.xml. Used as the primary guard throughout the
|
|
26
|
+
# updater pipeline to short-circuit non-wrapper paths.
|
|
27
|
+
requirements.any? { |req| req.source_string("type") == DISTRIBUTION_DEPENDENCY_TYPE }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require "base64"
|
|
4
5
|
require "nokogiri"
|
|
5
6
|
require "sorbet-runtime"
|
|
6
7
|
|
|
7
8
|
require "dependabot/file_fetchers"
|
|
8
9
|
require "dependabot/file_fetchers/base"
|
|
9
10
|
require "dependabot/file_filtering"
|
|
11
|
+
require "dependabot/experiments"
|
|
12
|
+
require "dependabot/maven/file_parser/wrapper_mojo"
|
|
10
13
|
|
|
11
14
|
module Dependabot
|
|
12
15
|
module Maven
|
|
@@ -17,6 +20,14 @@ module Dependabot
|
|
|
17
20
|
MODULE_SELECTOR = "project > modules > module, " \
|
|
18
21
|
"profile > modules > module"
|
|
19
22
|
|
|
23
|
+
WRAPPER_PROPERTIES_RELATIVE = ".mvn/wrapper/maven-wrapper.properties"
|
|
24
|
+
WRAPPER_JAR_RELATIVE = ".mvn/wrapper/maven-wrapper.jar"
|
|
25
|
+
WRAPPER_DOWNLOADER_RELATIVE = ".mvn/wrapper/MavenWrapperDownloader.java"
|
|
26
|
+
|
|
27
|
+
WRAPPER_UNIX_SCRIPTS = %w(mvnw mvnwDebug).freeze
|
|
28
|
+
WRAPPER_WINDOWS_SCRIPTS = %w(mvnw.cmd mvnwDebug.cmd).freeze
|
|
29
|
+
WRAPPER_ALL_SCRIPTS = T.let((WRAPPER_UNIX_SCRIPTS + WRAPPER_WINDOWS_SCRIPTS).freeze, T::Array[String])
|
|
30
|
+
|
|
20
31
|
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
|
21
32
|
def self.required_files_in?(filenames)
|
|
22
33
|
filenames.include?("pom.xml")
|
|
@@ -31,10 +42,13 @@ module Dependabot
|
|
|
31
42
|
def fetch_files
|
|
32
43
|
fetched_files = []
|
|
33
44
|
fetched_files << pom
|
|
34
|
-
|
|
45
|
+
poms = child_poms
|
|
46
|
+
fetched_files += poms
|
|
35
47
|
fetched_files += relative_path_parents(fetched_files)
|
|
36
48
|
fetched_files += targetfiles
|
|
37
49
|
fetched_files << extensions if extensions
|
|
50
|
+
# Pass already-fetched poms so all_wrapper_files does not re-fetch them.
|
|
51
|
+
fetched_files += all_wrapper_files([T.must(pom)] + poms)
|
|
38
52
|
|
|
39
53
|
# Filter excluded files from final collection
|
|
40
54
|
filtered_files = fetched_files.uniq.reject do |file|
|
|
@@ -46,6 +60,63 @@ module Dependabot
|
|
|
46
60
|
|
|
47
61
|
private
|
|
48
62
|
|
|
63
|
+
sig { params(dir: String).returns(T::Array[DependencyFile]) }
|
|
64
|
+
def wrapper_files_for_dir(dir)
|
|
65
|
+
return [] unless Dependabot::Experiments.enabled?(:maven_wrapper_updater)
|
|
66
|
+
|
|
67
|
+
# Strip leading "./" from root-level paths
|
|
68
|
+
properties_path = File.join(dir, WRAPPER_PROPERTIES_RELATIVE).delete_prefix("./")
|
|
69
|
+
properties = fetch_file_if_present(properties_path)
|
|
70
|
+
return [] unless properties
|
|
71
|
+
|
|
72
|
+
files = T.let([properties], T::Array[DependencyFile])
|
|
73
|
+
WRAPPER_ALL_SCRIPTS.each do |script|
|
|
74
|
+
script_path = dir == "." ? script : File.join(dir, script)
|
|
75
|
+
f = fetch_file_if_present(script_path)
|
|
76
|
+
f.mode = DependencyFile::Mode::EXECUTABLE if f && WRAPPER_UNIX_SCRIPTS.include?(script)
|
|
77
|
+
files << f if f
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
content = T.must(properties.content)
|
|
81
|
+
wrapper_url = FileParser::WrapperMojo.get_property_value(content, "wrapperUrl")
|
|
82
|
+
dist_type = FileParser::WrapperMojo.resolve_distribution_type(content, wrapper_url)
|
|
83
|
+
files + fetch_wrapper_artifact_files(dir, dist_type)
|
|
84
|
+
rescue Dependabot::DependencyFileNotFound
|
|
85
|
+
[]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
sig { params(dir: String, dist_type: String).returns(T::Array[DependencyFile]) }
|
|
89
|
+
def fetch_wrapper_artifact_files(dir, dist_type)
|
|
90
|
+
case dist_type
|
|
91
|
+
when "bin", "script"
|
|
92
|
+
jar_path = File.join(dir, WRAPPER_JAR_RELATIVE).delete_prefix("./")
|
|
93
|
+
jar = fetch_file_if_present(jar_path)
|
|
94
|
+
return [] unless jar
|
|
95
|
+
|
|
96
|
+
jar.content = Base64.encode64(T.must(jar.content)) if jar.content
|
|
97
|
+
jar.content_encoding = DependencyFile::ContentEncoding::BASE64
|
|
98
|
+
[jar]
|
|
99
|
+
when "source"
|
|
100
|
+
dl_path = File.join(dir, WRAPPER_DOWNLOADER_RELATIVE).delete_prefix("./")
|
|
101
|
+
downloader = fetch_file_if_present(dl_path)
|
|
102
|
+
downloader ? [downloader] : []
|
|
103
|
+
else
|
|
104
|
+
[]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
sig { params(poms: T::Array[DependencyFile]).returns(T::Array[DependencyFile]) }
|
|
109
|
+
def all_wrapper_files(poms)
|
|
110
|
+
seen_dirs = T.let(Set.new, T::Set[String])
|
|
111
|
+
poms.filter_map do |pom_file|
|
|
112
|
+
dir = File.dirname(pom_file.name)
|
|
113
|
+
next if seen_dirs.include?(dir)
|
|
114
|
+
|
|
115
|
+
seen_dirs << dir
|
|
116
|
+
wrapper_files_for_dir(dir)
|
|
117
|
+
end.flatten
|
|
118
|
+
end
|
|
119
|
+
|
|
49
120
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
50
121
|
def pom
|
|
51
122
|
@pom ||= T.let(fetch_file_from_host("pom.xml"), T.nilable(Dependabot::DependencyFile))
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Parses maven-wrapper.properties and emits Dependency objects for the two
|
|
5
|
+
# tracked Maven coordinates: org.apache.maven:apache-maven (the maven distribution)
|
|
6
|
+
# and org.apache.maven.wrapper:maven-wrapper (the wrapper plugin).
|
|
7
|
+
require "dependabot/dependency_requirement"
|
|
8
|
+
require "dependabot/maven/file_parser"
|
|
9
|
+
require "dependabot/maven/distributions"
|
|
10
|
+
|
|
11
|
+
module Dependabot
|
|
12
|
+
module Maven
|
|
13
|
+
class FileParser
|
|
14
|
+
# Model of the Maven Wrapper plugin's WrapperMojo goal. Mirrors the
|
|
15
|
+
# properties recognized by the upstream plugin:
|
|
16
|
+
# https://github.com/apache/maven-wrapper/blob/master/maven-wrapper-plugin/src/main/java/org/apache/maven/plugins/wrapper/WrapperMojo.java
|
|
17
|
+
class WrapperMojo
|
|
18
|
+
extend T::Sig
|
|
19
|
+
|
|
20
|
+
class WrapperProperties < T::Struct
|
|
21
|
+
# Resolved distributionUrl value (the raw value from the properties file).
|
|
22
|
+
# This value is mandatory
|
|
23
|
+
const :distribution_url, String
|
|
24
|
+
# The Maven Version extracted from the distributionUrl, e.g. "3.9.9"
|
|
25
|
+
const :distribution_version, String
|
|
26
|
+
|
|
27
|
+
# Value of distributionSha256Sum, or nil when the property is absent.
|
|
28
|
+
# Checksum verification is not mandatory
|
|
29
|
+
# Tracked as a second requirement on the Apache Maven dependency so the
|
|
30
|
+
# checksum is updated atomically with the version.
|
|
31
|
+
const :distribution_sha256_sum, T.nilable(String)
|
|
32
|
+
|
|
33
|
+
# Value of wrapperSha256Sum, or nil when the property is absent.
|
|
34
|
+
# Used to verify the wrapper JAR download
|
|
35
|
+
const :wrapper_sha256_sum, T.nilable(String)
|
|
36
|
+
|
|
37
|
+
# Version of the Maven Wrapper plugin, e.g. "3.3.4".
|
|
38
|
+
# Sourced from the first strategy that succeeds:
|
|
39
|
+
# - wrapperVersion property (>=3.3.1),
|
|
40
|
+
# - version segment of wrapperUrl JAR filename (<3.3.0), or
|
|
41
|
+
# - comment in the mvnw script body (3.3.0 only, see MWRAPPER-120 and MWRAPPER-134).
|
|
42
|
+
# This field is mandatory and raises if none of the sources yield a version.
|
|
43
|
+
const :wrapper_version, String
|
|
44
|
+
|
|
45
|
+
# The full JAR URL from the wrapperUrl property
|
|
46
|
+
# (e.g. "https://.../maven-wrapper-3.3.3.jar"), used to compute the
|
|
47
|
+
# new download URL for wrapperSha256Sum recomputation.
|
|
48
|
+
# Present in both old-format (wrapperUrl only) and new-format (wrapperVersion + wrapperUrl) files.
|
|
49
|
+
# nil only when the version was read from the mvnw script body (very old wrappers).
|
|
50
|
+
const :wrapper_url, T.nilable(String)
|
|
51
|
+
|
|
52
|
+
# Value of distributionType, controlling which binary artifacts are committed
|
|
53
|
+
# alongside the wrapper scripts:
|
|
54
|
+
# - "bin" (JAR present),
|
|
55
|
+
# - "script" (JAR present),
|
|
56
|
+
# - "only-script" (no JAR),
|
|
57
|
+
# - "source" (MavenWrapperDownloader.java present).
|
|
58
|
+
# Defaults to "bin" when the property is absent, matching pre-3.3.0 behavior.
|
|
59
|
+
const :distribution_type, String
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Extracts the version from a distributionUrl value (the resolved URL,
|
|
63
|
+
# not the raw properties line). Match the standard artifact filename so
|
|
64
|
+
# custom mirrors do not need to preserve Maven Central's directory layout.
|
|
65
|
+
DIST_URL_VERSION_REGEX = %r{
|
|
66
|
+
/apache-maven-(?<version>[^/?#]+)-(?:bin|src)\.(?:zip|tar\.gz)(?:[?#].*)?\z
|
|
67
|
+
}x
|
|
68
|
+
class UnparseableDistributionUrl < RuntimeError; end
|
|
69
|
+
|
|
70
|
+
sig do
|
|
71
|
+
params(
|
|
72
|
+
properties_file: DependencyFile,
|
|
73
|
+
script_files: T::Array[DependencyFile]
|
|
74
|
+
).returns(T::Array[Dependency])
|
|
75
|
+
end
|
|
76
|
+
def self.resolve_dependencies(properties_file, script_files: [])
|
|
77
|
+
content = properties_file.content
|
|
78
|
+
return [] unless content
|
|
79
|
+
|
|
80
|
+
distribution_url = get_property_value(content, "distributionUrl")
|
|
81
|
+
if distribution_url&.include?("mvnd")
|
|
82
|
+
Dependabot.logger.warn("Maven daemon (mvnd) distribution is not supported, skipping wrapper update")
|
|
83
|
+
return []
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
begin
|
|
87
|
+
props = load_properties(content, script_files: script_files)
|
|
88
|
+
rescue UnparseableDistributionUrl => e
|
|
89
|
+
Dependabot.logger.warn("#{e.message}, skipping wrapper update")
|
|
90
|
+
return []
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if props.wrapper_url&.include?("takari")
|
|
94
|
+
Dependabot.logger.warn("The Takari distribution is not supported, skipping wrapper update")
|
|
95
|
+
return []
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
file_name = properties_file.name
|
|
99
|
+
has_debug_scripts = debug_scripts?(script_files)
|
|
100
|
+
deps = [build_distribution_dependency(file_name, props, props.distribution_version, has_debug_scripts)]
|
|
101
|
+
deps << build_wrapper_dependency(
|
|
102
|
+
file_name, props, props.distribution_version, props.wrapper_version, has_debug_scripts
|
|
103
|
+
)
|
|
104
|
+
deps.compact
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
sig do
|
|
108
|
+
params(
|
|
109
|
+
file_name: String,
|
|
110
|
+
props: WrapperProperties,
|
|
111
|
+
dist_version: String,
|
|
112
|
+
has_debug_scripts: T::Boolean
|
|
113
|
+
).returns(Dependency)
|
|
114
|
+
end
|
|
115
|
+
def self.build_distribution_dependency(file_name, props, dist_version, has_debug_scripts)
|
|
116
|
+
Dependency.new(
|
|
117
|
+
name: Distributions::MAVEN_DISTRIBUTION_PACKAGE,
|
|
118
|
+
version: dist_version,
|
|
119
|
+
requirements: build_distribution_requirements(file_name, props, dist_version, has_debug_scripts),
|
|
120
|
+
package_manager: "maven"
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
sig do
|
|
125
|
+
params(
|
|
126
|
+
file_name: String,
|
|
127
|
+
props: WrapperProperties,
|
|
128
|
+
dist_version: String,
|
|
129
|
+
has_debug_scripts: T::Boolean
|
|
130
|
+
).returns(T::Array[Dependabot::DependencyRequirement])
|
|
131
|
+
end
|
|
132
|
+
def self.build_distribution_requirements(file_name, props, dist_version, has_debug_scripts)
|
|
133
|
+
metadata = T.let(
|
|
134
|
+
{
|
|
135
|
+
packaging_type: "pom",
|
|
136
|
+
wrapper_version: props.wrapper_version,
|
|
137
|
+
distribution_type: props.distribution_type,
|
|
138
|
+
distribution_version: dist_version,
|
|
139
|
+
include_debug_script: has_debug_scripts
|
|
140
|
+
},
|
|
141
|
+
Dependabot::DependencyRequirement::ObjectHash
|
|
142
|
+
)
|
|
143
|
+
metadata[:distribution_sha256_sum] = props.distribution_sha256_sum if props.distribution_sha256_sum
|
|
144
|
+
|
|
145
|
+
main_req = Dependabot::DependencyRequirement.create(
|
|
146
|
+
{
|
|
147
|
+
requirement: dist_version,
|
|
148
|
+
file: file_name,
|
|
149
|
+
source: {
|
|
150
|
+
type: Distributions::DISTRIBUTION_DEPENDENCY_TYPE,
|
|
151
|
+
url: props.distribution_url,
|
|
152
|
+
property: "distributionUrl"
|
|
153
|
+
},
|
|
154
|
+
groups: [],
|
|
155
|
+
# The Apache Maven distribution is not a JAR, but a POM is available
|
|
156
|
+
# We can use this POM to query for new versions using the existing update checker for maven
|
|
157
|
+
metadata: metadata
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
requirements = T.let([main_req], T::Array[Dependabot::DependencyRequirement])
|
|
162
|
+
requirements.concat(build_wrapper_url_requirement(file_name, props))
|
|
163
|
+
requirements
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
sig do
|
|
167
|
+
params(
|
|
168
|
+
file_name: String,
|
|
169
|
+
props: WrapperProperties
|
|
170
|
+
).returns(T::Array[Dependabot::DependencyRequirement])
|
|
171
|
+
end
|
|
172
|
+
def self.build_wrapper_url_requirement(file_name, props)
|
|
173
|
+
return [] unless props.wrapper_url
|
|
174
|
+
|
|
175
|
+
Dependabot.logger.debug "wrapperUrl is present #{props.wrapper_url} version=#{props.wrapper_version}"
|
|
176
|
+
metadata = T.let({}, Dependabot::DependencyRequirement::ObjectHash)
|
|
177
|
+
metadata[:wrapper_sha256_sum] = props.wrapper_sha256_sum if props.wrapper_sha256_sum
|
|
178
|
+
|
|
179
|
+
req = Dependabot::DependencyRequirement.create(
|
|
180
|
+
{
|
|
181
|
+
requirement: props.wrapper_version,
|
|
182
|
+
file: file_name,
|
|
183
|
+
source: {
|
|
184
|
+
type: Distributions::DISTRIBUTION_DEPENDENCY_TYPE,
|
|
185
|
+
property: "wrapperUrl",
|
|
186
|
+
url: props.wrapper_url
|
|
187
|
+
},
|
|
188
|
+
groups: []
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
req[:metadata] = metadata if metadata.any?
|
|
192
|
+
[req]
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
sig do
|
|
196
|
+
params(
|
|
197
|
+
file_name: String,
|
|
198
|
+
props: WrapperProperties,
|
|
199
|
+
dist_version: String,
|
|
200
|
+
wrapper_version: String,
|
|
201
|
+
has_debug_scripts: T::Boolean
|
|
202
|
+
).returns(Dependency)
|
|
203
|
+
end
|
|
204
|
+
def self.build_wrapper_dependency(file_name, props, dist_version, wrapper_version, has_debug_scripts)
|
|
205
|
+
metadata = T.let(
|
|
206
|
+
{
|
|
207
|
+
packaging_type: "pom",
|
|
208
|
+
distribution_version: dist_version,
|
|
209
|
+
wrapper_version: wrapper_version,
|
|
210
|
+
distribution_type: props.distribution_type,
|
|
211
|
+
include_debug_script: has_debug_scripts
|
|
212
|
+
},
|
|
213
|
+
Dependabot::DependencyRequirement::ObjectHash
|
|
214
|
+
)
|
|
215
|
+
Dependency.new(
|
|
216
|
+
name: Distributions::MAVEN_WRAPPER_PACKAGE,
|
|
217
|
+
version: props.wrapper_version,
|
|
218
|
+
requirements: [
|
|
219
|
+
Dependabot::DependencyRequirement.create(
|
|
220
|
+
{
|
|
221
|
+
requirement: props.wrapper_version,
|
|
222
|
+
file: file_name,
|
|
223
|
+
source: {
|
|
224
|
+
type: Distributions::DISTRIBUTION_DEPENDENCY_TYPE,
|
|
225
|
+
property: "wrapperVersion"
|
|
226
|
+
},
|
|
227
|
+
groups: [],
|
|
228
|
+
metadata: metadata
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
],
|
|
232
|
+
package_manager: "maven"
|
|
233
|
+
)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Resolves the distribution type, matching the maven-wrapper-plugin's own precedence:
|
|
237
|
+
# an explicit distributionType wins; otherwise the plugin defaults to "only-script" (since
|
|
238
|
+
# 3.2.0). Legacy pre-3.3.0 files omit distributionType but carry a wrapperUrl pointing at the
|
|
239
|
+
# maven-wrapper JAR, which means a JAR-based ("bin") setup, so we keep that for them.
|
|
240
|
+
# https://maven.apache.org/tools/wrapper/maven-wrapper-plugin/wrapper-mojo.html
|
|
241
|
+
sig { params(content: String, wrapper_url: T.nilable(String)).returns(String) }
|
|
242
|
+
def self.resolve_distribution_type(content, wrapper_url)
|
|
243
|
+
explicit = get_property_value(content, "distributionType")
|
|
244
|
+
return explicit if explicit
|
|
245
|
+
|
|
246
|
+
wrapper_url ? "bin" : "only-script"
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
sig { params(script_files: T::Array[DependencyFile]).returns(T::Boolean) }
|
|
250
|
+
def self.debug_scripts?(script_files)
|
|
251
|
+
debug_scripts = %w(mvnwDebug mvnwDebug.cmd)
|
|
252
|
+
script_files.any? { |f| debug_scripts.any? { |s| f.name.end_with?(s) } }
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
sig { params(content: String, script_files: T::Array[DependencyFile]).returns(WrapperProperties) }
|
|
256
|
+
def self.load_properties(content, script_files: [])
|
|
257
|
+
distribution_url = get_property_value!(content, "distributionUrl")
|
|
258
|
+
distribution_version = extract_distribution_version(distribution_url)
|
|
259
|
+
distribution_sha256_sum = get_property_value(content, "distributionSha256Sum")
|
|
260
|
+
wrapper_url = get_property_value(content, "wrapperUrl")
|
|
261
|
+
wrapper_sha256_sum = get_property_value(content, "wrapperSha256Sum")
|
|
262
|
+
distribution_type = resolve_distribution_type(content, wrapper_url)
|
|
263
|
+
wrapper_version = resolve_wrapper_version(content, wrapper_url, script_files)
|
|
264
|
+
|
|
265
|
+
WrapperProperties.new(
|
|
266
|
+
distribution_url: distribution_url,
|
|
267
|
+
distribution_version: distribution_version,
|
|
268
|
+
distribution_sha256_sum: distribution_sha256_sum,
|
|
269
|
+
wrapper_sha256_sum: wrapper_sha256_sum,
|
|
270
|
+
wrapper_version: wrapper_version,
|
|
271
|
+
wrapper_url: wrapper_url,
|
|
272
|
+
distribution_type: distribution_type
|
|
273
|
+
)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
sig { params(content: String).returns(String) }
|
|
277
|
+
def self.extract_distribution_version(content)
|
|
278
|
+
match = content.match(DIST_URL_VERSION_REGEX)
|
|
279
|
+
unless match && match[:version]
|
|
280
|
+
raise UnparseableDistributionUrl, "Could not extract Maven version from distributionUrl"
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
T.must(match[:version])
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
sig { params(content: String, target_key: String).returns(T.nilable(String)) }
|
|
287
|
+
def self.get_property_value(content, target_key)
|
|
288
|
+
# 1. Handle Java line continuations (the backslash edge case)
|
|
289
|
+
# We join lines ending in \ before splitting into an array
|
|
290
|
+
normalized_content = content.gsub(/\\\n\s*/, "")
|
|
291
|
+
|
|
292
|
+
# 2. Escape the key for Regex safety
|
|
293
|
+
escaped_key = Regexp.escape(target_key)
|
|
294
|
+
|
|
295
|
+
# Start of line -> Key -> optional space -> delimiter (= or :) -> value.
|
|
296
|
+
# Bounded to spaces/tabs (not \s) so the per-line match can't backtrack polynomially.
|
|
297
|
+
pattern = /^[ \t]*#{escaped_key}[ \t]*[=:][ \t]*(.*)$/
|
|
298
|
+
|
|
299
|
+
normalized_content.lines.each do |line|
|
|
300
|
+
next if line.start_with?("#", "!") # Skip Java comments
|
|
301
|
+
|
|
302
|
+
if (match = line.match(pattern))
|
|
303
|
+
return decode_property_value(T.must(match[1]).strip)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
nil
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Decodes Java properties escapes so URLs are usable by URI parsing, registry matching, and
|
|
310
|
+
# HTTP clients. Maven commonly writes URL schemes as `https\://` in legacy wrapper files.
|
|
311
|
+
sig { params(value: String).returns(String) }
|
|
312
|
+
def self.decode_property_value(value)
|
|
313
|
+
value.gsub(/\\(?:u(?<unicode>[0-9a-fA-F]{4})|(?<escaped>.))/) do
|
|
314
|
+
unicode = Regexp.last_match(:unicode)
|
|
315
|
+
next [unicode.hex].pack("U") if unicode
|
|
316
|
+
|
|
317
|
+
case Regexp.last_match(:escaped)
|
|
318
|
+
when "t" then "\t"
|
|
319
|
+
when "n" then "\n"
|
|
320
|
+
when "r" then "\r"
|
|
321
|
+
when "f" then "\f"
|
|
322
|
+
else Regexp.last_match(:escaped)
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
sig { params(content: String, target_key: String).returns(String) }
|
|
328
|
+
def self.get_property_value!(content, target_key)
|
|
329
|
+
value = get_property_value(content, target_key)
|
|
330
|
+
|
|
331
|
+
raise "Missing mandatory property: #{target_key}" if value.nil?
|
|
332
|
+
|
|
333
|
+
value
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
private_class_method :build_distribution_dependency
|
|
337
|
+
private_class_method :build_wrapper_dependency
|
|
338
|
+
private_class_method :build_distribution_requirements
|
|
339
|
+
private_class_method :build_wrapper_url_requirement
|
|
340
|
+
private_class_method :decode_property_value
|
|
341
|
+
|
|
342
|
+
# Matches the human-readable banner embedded in mvnw / mvnw.cmd, e.g.:
|
|
343
|
+
# "Apache Maven Wrapper startup script, version 3.3.2"
|
|
344
|
+
# "Apache Maven Wrapper startup batch script, version 3.3.2"
|
|
345
|
+
SCRIPT_VERSION_REGEX = /
|
|
346
|
+
Apache \s Maven \s Wrapper \s startup \s (?:batch \s)?
|
|
347
|
+
script, \s version \s
|
|
348
|
+
(?<version>\d+\.\d+(?:\.\d+)?)
|
|
349
|
+
/x
|
|
350
|
+
|
|
351
|
+
sig do
|
|
352
|
+
params(
|
|
353
|
+
content: String,
|
|
354
|
+
wrapper_url: T.nilable(String),
|
|
355
|
+
script_files: T::Array[DependencyFile]
|
|
356
|
+
).returns(String)
|
|
357
|
+
end
|
|
358
|
+
def self.resolve_wrapper_version(content, wrapper_url, script_files)
|
|
359
|
+
version = get_property_value(content, "wrapperVersion")
|
|
360
|
+
return version if version
|
|
361
|
+
|
|
362
|
+
version = parse_version_from_wrapper_url(wrapper_url) if wrapper_url
|
|
363
|
+
return version if version
|
|
364
|
+
|
|
365
|
+
if script_files.any?
|
|
366
|
+
Dependabot.logger.warn "Maven Wrapper with no wrapperVersion or wrapperUrl in properties file"
|
|
367
|
+
version = load_wrapper_version_from_scripts(script_files)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
if version.nil?
|
|
371
|
+
raise "Could not determine Maven Wrapper version from wrapperVersion, wrapperUrl, or script files"
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
version
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
sig { params(url: String).returns(T.nilable(String)) }
|
|
378
|
+
def self.parse_version_from_wrapper_url(url)
|
|
379
|
+
match = url.match(/-(?<version>\d+\.\d+(?:\.\d+)?(?:-\w+)*)(?:-bin)?\.jar/)
|
|
380
|
+
match&.[](:version)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Extracts the Maven Wrapper version declared in the mvnw / mvnw.cmd
|
|
384
|
+
# shell scripts. Unix scripts (mvnw) are checked before Windows scripts (mvnw.cmd)
|
|
385
|
+
# if neither contains the banner, nil is returned and the caller falls back to other sources.
|
|
386
|
+
sig { params(script_files: T::Array[DependencyFile]).returns(T.nilable(String)) }
|
|
387
|
+
def self.load_wrapper_version_from_scripts(script_files)
|
|
388
|
+
# Preferred order: Unix script first, Windows script as fallback.
|
|
389
|
+
windows_scripts, unix_scripts = script_files.partition { |f| f.name.end_with?(".cmd") }
|
|
390
|
+
unix_scripts.chain(windows_scripts).each do |file|
|
|
391
|
+
next unless file.content
|
|
392
|
+
|
|
393
|
+
T.must(file.content).each_line do |line|
|
|
394
|
+
m = line.match(SCRIPT_VERSION_REGEX)
|
|
395
|
+
return m[:version] if m
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
nil
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
private_class_method :resolve_wrapper_version
|
|
402
|
+
private_class_method :parse_version_from_wrapper_url
|
|
403
|
+
private_class_method :load_wrapper_version_from_scripts
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|