dependabot-gradle 0.84.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/dependabot/gradle.rb +11 -0
- data/lib/dependabot/gradle/file_fetcher.rb +57 -0
- data/lib/dependabot/gradle/file_fetcher/settings_file_parser.rb +64 -0
- data/lib/dependabot/gradle/file_parser.rb +237 -0
- data/lib/dependabot/gradle/file_parser/property_value_finder.rb +88 -0
- data/lib/dependabot/gradle/file_parser/repositories_finder.rb +143 -0
- data/lib/dependabot/gradle/file_updater.rb +177 -0
- data/lib/dependabot/gradle/file_updater/dependency_set_updater.rb +64 -0
- data/lib/dependabot/gradle/file_updater/property_value_updater.rb +56 -0
- data/lib/dependabot/gradle/metadata_finder.rb +174 -0
- data/lib/dependabot/gradle/requirement.rb +110 -0
- data/lib/dependabot/gradle/update_checker.rb +149 -0
- data/lib/dependabot/gradle/update_checker/multi_dependency_updater.rb +103 -0
- data/lib/dependabot/gradle/update_checker/requirements_updater.rb +90 -0
- data/lib/dependabot/gradle/update_checker/version_finder.rb +181 -0
- data/lib/dependabot/gradle/version.rb +181 -0
- metadata +186 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/utils"
|
4
|
+
require "dependabot/gradle/version"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module Gradle
|
8
|
+
class Requirement < Gem::Requirement
|
9
|
+
quoted = OPS.keys.map { |k| Regexp.quote k }.join("|")
|
10
|
+
PATTERN_RAW =
|
11
|
+
"\\s*(#{quoted})?\\s*(#{Gradle::Version::VERSION_PATTERN})\\s*"
|
12
|
+
PATTERN = /\A#{PATTERN_RAW}\z/.freeze
|
13
|
+
|
14
|
+
def self.parse(obj)
|
15
|
+
return ["=", Gradle::Version.new(obj.to_s)] if obj.is_a?(Gem::Version)
|
16
|
+
|
17
|
+
unless (matches = PATTERN.match(obj.to_s))
|
18
|
+
msg = "Illformed requirement [#{obj.inspect}]"
|
19
|
+
raise BadRequirementError, msg
|
20
|
+
end
|
21
|
+
|
22
|
+
return DefaultRequirement if matches[1] == ">=" && matches[2] == "0"
|
23
|
+
|
24
|
+
[matches[1] || "=", Gradle::Version.new(matches[2])]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.requirements_array(requirement_string)
|
28
|
+
split_java_requirement(requirement_string).map do |str|
|
29
|
+
new(str)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(*requirements)
|
34
|
+
requirements = requirements.flatten.flat_map do |req_string|
|
35
|
+
convert_java_constraint_to_ruby_constraint(req_string)
|
36
|
+
end
|
37
|
+
|
38
|
+
super(requirements)
|
39
|
+
end
|
40
|
+
|
41
|
+
def satisfied_by?(version)
|
42
|
+
version = Gradle::Version.new(version.to_s)
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def self.split_java_requirement(req_string)
|
49
|
+
req_string.split(/(?<=\]|\)),/).flat_map do |str|
|
50
|
+
next str if str.start_with?("(", "[")
|
51
|
+
|
52
|
+
exacts, *rest = str.split(/,(?=\[|\()/)
|
53
|
+
[*exacts.split(","), *rest]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
private_class_method :split_java_requirement
|
57
|
+
|
58
|
+
def convert_java_constraint_to_ruby_constraint(req_string)
|
59
|
+
return unless req_string
|
60
|
+
|
61
|
+
if self.class.send(:split_java_requirement, req_string).count > 1
|
62
|
+
raise "Can't convert multiple Java reqs to a single Ruby one"
|
63
|
+
end
|
64
|
+
|
65
|
+
if req_string&.include?(",")
|
66
|
+
return convert_java_range_to_ruby_range(req_string)
|
67
|
+
end
|
68
|
+
|
69
|
+
convert_java_equals_req_to_ruby(req_string)
|
70
|
+
end
|
71
|
+
|
72
|
+
def convert_java_range_to_ruby_range(req_string)
|
73
|
+
lower_b, upper_b = req_string.split(",").map(&:strip)
|
74
|
+
|
75
|
+
lower_b =
|
76
|
+
if ["(", "["].include?(lower_b) then nil
|
77
|
+
elsif lower_b.start_with?("(") then "> #{lower_b.sub(/\(\s*/, '')}"
|
78
|
+
else ">= #{lower_b.sub(/\[\s*/, '').strip}"
|
79
|
+
end
|
80
|
+
|
81
|
+
upper_b =
|
82
|
+
if [")", "]"].include?(upper_b) then nil
|
83
|
+
elsif upper_b.end_with?(")") then "< #{upper_b.sub(/\s*\)/, '')}"
|
84
|
+
else "<= #{upper_b.sub(/\s*\]/, '').strip}"
|
85
|
+
end
|
86
|
+
|
87
|
+
[lower_b, upper_b].compact
|
88
|
+
end
|
89
|
+
|
90
|
+
def convert_java_equals_req_to_ruby(req_string)
|
91
|
+
return convert_wildcard_req(req_string) if req_string&.include?("+")
|
92
|
+
|
93
|
+
# If a soft requirement is being used, treat it as an equality matcher
|
94
|
+
return req_string unless req_string&.start_with?("[")
|
95
|
+
|
96
|
+
req_string.gsub(/[\[\]\(\)]/, "")
|
97
|
+
end
|
98
|
+
|
99
|
+
def convert_wildcard_req(req_string)
|
100
|
+
version = req_string.gsub(/(?:\.|^)\+/, "")
|
101
|
+
return ">= 0" if version.empty?
|
102
|
+
|
103
|
+
"~> #{version}.0"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Dependabot::Utils.
|
110
|
+
register_requirement_class("gradle", Dependabot::Gradle::Requirement)
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/update_checkers"
|
4
|
+
require "dependabot/update_checkers/base"
|
5
|
+
require "dependabot/gradle/file_parser"
|
6
|
+
|
7
|
+
module Dependabot
|
8
|
+
module Gradle
|
9
|
+
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
10
|
+
require_relative "update_checker/requirements_updater"
|
11
|
+
require_relative "update_checker/version_finder"
|
12
|
+
require_relative "update_checker/multi_dependency_updater"
|
13
|
+
|
14
|
+
def latest_version
|
15
|
+
latest_version_details&.fetch(:version)
|
16
|
+
end
|
17
|
+
|
18
|
+
def latest_resolvable_version
|
19
|
+
# TODO: Resolve the build.gradle to find the latest version we could
|
20
|
+
# update to without updating any other dependencies at the same time.
|
21
|
+
#
|
22
|
+
# The above is hard. Currently we just return the latest version and
|
23
|
+
# hope (hence this package manager is in beta!)
|
24
|
+
return nil if version_comes_from_multi_dependency_property?
|
25
|
+
return nil if version_comes_from_dependency_set?
|
26
|
+
|
27
|
+
latest_version
|
28
|
+
end
|
29
|
+
|
30
|
+
def latest_resolvable_version_with_no_unlock
|
31
|
+
# Irrelevant, since Gradle has a single dependency file.
|
32
|
+
#
|
33
|
+
# For completeness we ought to resolve the build.gradle and return the
|
34
|
+
# latest version that satisfies the current constraint AND any
|
35
|
+
# constraints placed on it by other dependencies. Seeing as we're
|
36
|
+
# never going to take any action as a result, though, we just return
|
37
|
+
# nil.
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def updated_requirements
|
42
|
+
property_names =
|
43
|
+
declarations_using_a_property.
|
44
|
+
map { |req| req.dig(:metadata, :property_name) }
|
45
|
+
|
46
|
+
RequirementsUpdater.new(
|
47
|
+
requirements: dependency.requirements,
|
48
|
+
latest_version: latest_version&.to_s,
|
49
|
+
source_url: latest_version_details&.fetch(:source_url),
|
50
|
+
properties_to_update: property_names
|
51
|
+
).updated_requirements
|
52
|
+
end
|
53
|
+
|
54
|
+
def requirements_unlocked_or_can_be?
|
55
|
+
# If the dependency version come from a property we couldn't
|
56
|
+
# interpolate then there's nothing we can do.
|
57
|
+
!dependency.version.include?("$")
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def latest_version_resolvable_with_full_unlock?
|
63
|
+
unless version_comes_from_multi_dependency_property? ||
|
64
|
+
version_comes_from_dependency_set?
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
|
68
|
+
multi_dependency_updater.update_possible?
|
69
|
+
end
|
70
|
+
|
71
|
+
def updated_dependencies_after_full_unlock
|
72
|
+
multi_dependency_updater.updated_dependencies
|
73
|
+
end
|
74
|
+
|
75
|
+
def numeric_version_up_to_date?
|
76
|
+
return false unless version_class.correct?(dependency.version)
|
77
|
+
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
81
|
+
def numeric_version_can_update?(requirements_to_unlock:)
|
82
|
+
return false unless version_class.correct?(dependency.version)
|
83
|
+
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
def latest_version_details
|
88
|
+
@latest_version_details ||= version_finder.latest_version_details
|
89
|
+
end
|
90
|
+
|
91
|
+
def version_finder
|
92
|
+
@version_finder ||=
|
93
|
+
VersionFinder.new(
|
94
|
+
dependency: dependency,
|
95
|
+
dependency_files: dependency_files,
|
96
|
+
ignored_versions: ignored_versions
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def multi_dependency_updater
|
101
|
+
@multi_dependency_updater ||=
|
102
|
+
MultiDependencyUpdater.new(
|
103
|
+
dependency: dependency,
|
104
|
+
dependency_files: dependency_files,
|
105
|
+
target_version_details: latest_version_details,
|
106
|
+
ignored_versions: ignored_versions
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
def version_comes_from_multi_dependency_property?
|
111
|
+
declarations_using_a_property.any? do |requirement|
|
112
|
+
property_name = requirement.fetch(:metadata).fetch(:property_name)
|
113
|
+
|
114
|
+
all_property_based_dependencies.any? do |dep|
|
115
|
+
next false if dep.name == dependency.name
|
116
|
+
|
117
|
+
dep.requirements.any? do |req|
|
118
|
+
req.dig(:metadata, :property_name) == property_name
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def version_comes_from_dependency_set?
|
125
|
+
dependency.requirements.any? do |req|
|
126
|
+
req.dig(:metadata, :dependency_set)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def declarations_using_a_property
|
131
|
+
@declarations_using_a_property ||=
|
132
|
+
dependency.requirements.
|
133
|
+
select { |req| req.dig(:metadata, :property_name) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def all_property_based_dependencies
|
137
|
+
@all_property_based_dependencies ||=
|
138
|
+
Gradle::FileParser.new(
|
139
|
+
dependency_files: dependency_files,
|
140
|
+
source: nil
|
141
|
+
).parse.select do |dep|
|
142
|
+
dep.requirements.any? { |req| req.dig(:metadata, :property_name) }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
Dependabot::UpdateCheckers.register("gradle", Dependabot::Gradle::UpdateChecker)
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/gradle/file_parser"
|
4
|
+
require "dependabot/gradle/update_checker"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module Gradle
|
8
|
+
class UpdateChecker
|
9
|
+
class MultiDependencyUpdater
|
10
|
+
require_relative "version_finder"
|
11
|
+
require_relative "requirements_updater"
|
12
|
+
|
13
|
+
def initialize(dependency:, dependency_files:,
|
14
|
+
target_version_details:, ignored_versions:)
|
15
|
+
@dependency = dependency
|
16
|
+
@dependency_files = dependency_files
|
17
|
+
@target_version = target_version_details&.fetch(:version)
|
18
|
+
@source_url = target_version_details&.fetch(:source_url)
|
19
|
+
@ignored_versions = ignored_versions
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_possible?
|
23
|
+
return false unless target_version
|
24
|
+
|
25
|
+
@update_possible ||=
|
26
|
+
dependencies_to_update.all? do |dep|
|
27
|
+
VersionFinder.new(
|
28
|
+
dependency: dep,
|
29
|
+
dependency_files: dependency_files,
|
30
|
+
ignored_versions: ignored_versions
|
31
|
+
).versions.
|
32
|
+
map { |v| v.fetch(:version) }.
|
33
|
+
include?(target_version)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def updated_dependencies
|
38
|
+
raise "Update not possible!" unless update_possible?
|
39
|
+
|
40
|
+
@updated_dependencies ||=
|
41
|
+
dependencies_to_update.map do |dep|
|
42
|
+
Dependency.new(
|
43
|
+
name: dep.name,
|
44
|
+
version: target_version.to_s,
|
45
|
+
requirements: updated_requirements(dep),
|
46
|
+
previous_version: dep.version,
|
47
|
+
previous_requirements: dep.requirements,
|
48
|
+
package_manager: dep.package_manager
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :dependency, :dependency_files, :target_version,
|
56
|
+
:source_url, :ignored_versions
|
57
|
+
|
58
|
+
def dependencies_to_update
|
59
|
+
@dependencies_to_update ||=
|
60
|
+
Gradle::FileParser.new(
|
61
|
+
dependency_files: dependency_files,
|
62
|
+
source: nil
|
63
|
+
).parse.select do |dep|
|
64
|
+
dep.requirements.any? do |r|
|
65
|
+
tmp_p_name = r.dig(:metadata, :property_name)
|
66
|
+
tmp_dep_set = r.dig(:metadata, :dependency_set)
|
67
|
+
next true if property_name && tmp_p_name == property_name
|
68
|
+
|
69
|
+
dependency_set && tmp_dep_set == dependency_set
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def property_name
|
75
|
+
@property_name ||= dependency.requirements.
|
76
|
+
find { |r| r.dig(:metadata, :property_name) }&.
|
77
|
+
dig(:metadata, :property_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def dependency_set
|
81
|
+
@dependency_set ||= dependency.requirements.
|
82
|
+
find { |r| r.dig(:metadata, :dependency_set) }&.
|
83
|
+
dig(:metadata, :dependency_set)
|
84
|
+
end
|
85
|
+
|
86
|
+
def pom
|
87
|
+
dependency_files.find { |f| f.name == "pom.xml" }
|
88
|
+
end
|
89
|
+
|
90
|
+
def updated_requirements(dep)
|
91
|
+
@updated_requirements ||= {}
|
92
|
+
@updated_requirements[dep.name] ||=
|
93
|
+
RequirementsUpdater.new(
|
94
|
+
requirements: dep.requirements,
|
95
|
+
latest_version: target_version.to_s,
|
96
|
+
source_url: source_url,
|
97
|
+
properties_to_update: [property_name].compact
|
98
|
+
).updated_requirements
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#######################################################
|
4
|
+
# For more details on Maven version constraints, see: #
|
5
|
+
# https://maven.apache.org/pom.html#Dependencies #
|
6
|
+
#######################################################
|
7
|
+
|
8
|
+
require "dependabot/gradle/update_checker"
|
9
|
+
require "dependabot/gradle/version"
|
10
|
+
require "dependabot/gradle/requirement"
|
11
|
+
|
12
|
+
module Dependabot
|
13
|
+
module Gradle
|
14
|
+
class UpdateChecker
|
15
|
+
class RequirementsUpdater
|
16
|
+
def initialize(requirements:, latest_version:, source_url:,
|
17
|
+
properties_to_update:)
|
18
|
+
@requirements = requirements
|
19
|
+
@source_url = source_url
|
20
|
+
@properties_to_update = properties_to_update
|
21
|
+
return unless latest_version
|
22
|
+
|
23
|
+
@latest_version = version_class.new(latest_version)
|
24
|
+
end
|
25
|
+
|
26
|
+
def updated_requirements
|
27
|
+
return requirements unless latest_version
|
28
|
+
|
29
|
+
# Note: Order is important here. The FileUpdater needs the updated
|
30
|
+
# requirement at index `i` to correspond to the previous requirement
|
31
|
+
# at the same index.
|
32
|
+
requirements.map do |req|
|
33
|
+
next req if req.fetch(:requirement).nil?
|
34
|
+
next req if req.fetch(:requirement).include?(",")
|
35
|
+
|
36
|
+
property_name = req.dig(:metadata, :property_name)
|
37
|
+
if property_name && !properties_to_update.include?(property_name)
|
38
|
+
next req
|
39
|
+
end
|
40
|
+
|
41
|
+
new_req = update_requirement(req[:requirement])
|
42
|
+
req.merge(requirement: new_req, source: updated_source)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
attr_reader :requirements, :latest_version, :source_url,
|
49
|
+
:properties_to_update
|
50
|
+
|
51
|
+
def update_requirement(req_string)
|
52
|
+
if req_string.include?(".+")
|
53
|
+
update_dynamic_requirement(req_string)
|
54
|
+
else
|
55
|
+
# Since range requirements are excluded this must be exact
|
56
|
+
update_exact_requirement(req_string)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_exact_requirement(req_string)
|
61
|
+
old_version = requirement_class.new(req_string).
|
62
|
+
requirements.first.last
|
63
|
+
req_string.gsub(old_version.to_s, latest_version.to_s)
|
64
|
+
end
|
65
|
+
|
66
|
+
# This is really only a Gradle thing, but Gradle relies on this
|
67
|
+
# RequirementsUpdater too
|
68
|
+
def update_dynamic_requirement(req_string)
|
69
|
+
precision = req_string.split(".").take_while { |s| s != "+" }.count
|
70
|
+
|
71
|
+
version_parts = latest_version.segments.first(precision)
|
72
|
+
|
73
|
+
version_parts.join(".") + ".+"
|
74
|
+
end
|
75
|
+
|
76
|
+
def version_class
|
77
|
+
Gradle::Version
|
78
|
+
end
|
79
|
+
|
80
|
+
def requirement_class
|
81
|
+
Gradle::Requirement
|
82
|
+
end
|
83
|
+
|
84
|
+
def updated_source
|
85
|
+
{ type: "maven_repo", url: source_url }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|