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,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/gradle/file_parser"
|
4
|
+
|
5
|
+
module Dependabot
|
6
|
+
module Gradle
|
7
|
+
class FileParser
|
8
|
+
class RepositoriesFinder
|
9
|
+
# The Central Repo doesn't have special status for Gradle, but until
|
10
|
+
# we're confident we're selecting repos correctly it's wise to include
|
11
|
+
# it as a default.
|
12
|
+
CENTRAL_REPO_URL = "https://repo.maven.apache.org/maven2"
|
13
|
+
|
14
|
+
REPOSITORIES_BLOCK_START = /(?:^|\s)repositories\s*\{/.freeze
|
15
|
+
MAVEN_REPO_REGEX =
|
16
|
+
/maven\s*\{[^\}]*\surl[\s\(]\s*['"](?<url>[^'"]+)['"]/.freeze
|
17
|
+
|
18
|
+
def initialize(dependency_files:, target_dependency_file:)
|
19
|
+
@dependency_files = dependency_files
|
20
|
+
@target_dependency_file = target_dependency_file
|
21
|
+
raise "No target file!" unless target_dependency_file
|
22
|
+
end
|
23
|
+
|
24
|
+
def repository_urls
|
25
|
+
repository_urls = []
|
26
|
+
repository_urls += inherited_repository_urls
|
27
|
+
repository_urls += own_buildfile_repository_urls
|
28
|
+
repository_urls = repository_urls.uniq
|
29
|
+
|
30
|
+
return repository_urls unless repository_urls.empty?
|
31
|
+
|
32
|
+
[CENTRAL_REPO_URL]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :dependency_files, :target_dependency_file
|
38
|
+
|
39
|
+
def inherited_repository_urls
|
40
|
+
return [] unless top_level_buildfile
|
41
|
+
|
42
|
+
buildfile_content = comment_free_content(top_level_buildfile)
|
43
|
+
subproject_blocks = []
|
44
|
+
|
45
|
+
buildfile_content.scan(/(?:^|\s)allprojects\s*\{/) do
|
46
|
+
mtch = Regexp.last_match
|
47
|
+
subproject_blocks <<
|
48
|
+
mtch.post_match[0..closing_bracket_index(mtch.post_match)]
|
49
|
+
end
|
50
|
+
|
51
|
+
if top_level_buildfile != target_dependency_file
|
52
|
+
buildfile_content.scan(/(?:^|\s)subprojects\s*\{/) do
|
53
|
+
mtch = Regexp.last_match
|
54
|
+
subproject_blocks <<
|
55
|
+
mtch.post_match[0..closing_bracket_index(mtch.post_match)]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
repository_urls_from(subproject_blocks.join("\n"))
|
60
|
+
end
|
61
|
+
|
62
|
+
def own_buildfile_repository_urls
|
63
|
+
buildfile_content = comment_free_content(target_dependency_file)
|
64
|
+
|
65
|
+
buildfile_content.dup.scan(/(?:^|\s)subprojects\s*\{/) do
|
66
|
+
mtch = Regexp.last_match
|
67
|
+
buildfile_content.gsub!(
|
68
|
+
mtch.post_match[0..closing_bracket_index(mtch.post_match)],
|
69
|
+
""
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
repository_urls_from(buildfile_content)
|
74
|
+
end
|
75
|
+
|
76
|
+
def repository_urls_from(buildfile_content)
|
77
|
+
repository_urls = []
|
78
|
+
|
79
|
+
repository_blocks = []
|
80
|
+
buildfile_content.scan(REPOSITORIES_BLOCK_START) do
|
81
|
+
mtch = Regexp.last_match
|
82
|
+
repository_blocks <<
|
83
|
+
mtch.post_match[0..closing_bracket_index(mtch.post_match)]
|
84
|
+
end
|
85
|
+
|
86
|
+
repository_blocks.each do |block|
|
87
|
+
if block.include?(" google(")
|
88
|
+
repository_urls << "https://maven.google.com/"
|
89
|
+
end
|
90
|
+
|
91
|
+
if block.include?(" mavenCentral(")
|
92
|
+
repository_urls << "https://repo.maven.apache.org/maven2/"
|
93
|
+
end
|
94
|
+
|
95
|
+
if block.include?(" jcenter(")
|
96
|
+
repository_urls << "https://jcenter.bintray.com/"
|
97
|
+
end
|
98
|
+
|
99
|
+
block.scan(MAVEN_REPO_REGEX) do
|
100
|
+
repository_urls << Regexp.last_match.named_captures.fetch("url")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
repository_urls.
|
105
|
+
map { |url| url.strip.gsub(%r{/$}, "") }.
|
106
|
+
select { |url| valid_url?(url) }.
|
107
|
+
uniq
|
108
|
+
end
|
109
|
+
|
110
|
+
def closing_bracket_index(string)
|
111
|
+
closes_required = 1
|
112
|
+
|
113
|
+
string.chars.each_with_index do |char, index|
|
114
|
+
closes_required += 1 if char == "{"
|
115
|
+
closes_required -= 1 if char == "}"
|
116
|
+
return index if closes_required.zero?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def valid_url?(url)
|
121
|
+
# Reject non-http URLs because they're probably parsing mistakes
|
122
|
+
return false unless url.start_with?("http")
|
123
|
+
|
124
|
+
URI.parse(url)
|
125
|
+
true
|
126
|
+
rescue URI::InvalidURIError
|
127
|
+
false
|
128
|
+
end
|
129
|
+
|
130
|
+
def comment_free_content(buildfile)
|
131
|
+
buildfile.content.
|
132
|
+
gsub(%r{(?<=^|\s)//.*$}, "\n").
|
133
|
+
gsub(%r{(?<=^|\s)/\*.*?\*/}m, "")
|
134
|
+
end
|
135
|
+
|
136
|
+
def top_level_buildfile
|
137
|
+
@top_level_buildfile ||=
|
138
|
+
dependency_files.find { |f| f.name == "build.gradle" }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/file_updaters"
|
4
|
+
require "dependabot/file_updaters/base"
|
5
|
+
require "dependabot/gradle/file_parser"
|
6
|
+
|
7
|
+
module Dependabot
|
8
|
+
module Gradle
|
9
|
+
class FileUpdater < Dependabot::FileUpdaters::Base
|
10
|
+
require_relative "file_updater/dependency_set_updater"
|
11
|
+
require_relative "file_updater/property_value_updater"
|
12
|
+
|
13
|
+
def self.updated_files_regex
|
14
|
+
[/^build\.gradle$/, %r{/build\.gradle$}]
|
15
|
+
end
|
16
|
+
|
17
|
+
def updated_dependency_files
|
18
|
+
updated_files = buildfiles.dup
|
19
|
+
|
20
|
+
# Loop through each of the changed requirements, applying changes to
|
21
|
+
# all buildfiles for that change. Note that the logic is different
|
22
|
+
# here to other languages because Java has property inheritance across
|
23
|
+
# files (although we're not supporting it for gradle yet).
|
24
|
+
dependencies.each do |dependency|
|
25
|
+
updated_files = update_buildfiles_for_dependency(
|
26
|
+
buildfiles: updated_files,
|
27
|
+
dependency: dependency
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
updated_files = updated_files.reject { |f| buildfiles.include?(f) }
|
32
|
+
|
33
|
+
raise "No files changed!" if updated_files.none?
|
34
|
+
|
35
|
+
updated_files
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def check_required_files
|
41
|
+
raise "No build.gradle!" unless get_original_file("build.gradle")
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_buildfiles_for_dependency(buildfiles:, dependency:)
|
45
|
+
files = buildfiles.dup
|
46
|
+
|
47
|
+
# The UpdateChecker ensures the order of requirements is preserved
|
48
|
+
# when updating, so we can zip them together in new/old pairs.
|
49
|
+
reqs = dependency.requirements.zip(dependency.previous_requirements).
|
50
|
+
reject { |new_req, old_req| new_req == old_req }
|
51
|
+
|
52
|
+
# Loop through each changed requirement and update the buildfiles
|
53
|
+
reqs.each do |new_req, old_req|
|
54
|
+
raise "Bad req match" unless new_req[:file] == old_req[:file]
|
55
|
+
next if new_req[:requirement] == old_req[:requirement]
|
56
|
+
|
57
|
+
buildfile = files.find { |f| f.name == new_req.fetch(:file) }
|
58
|
+
|
59
|
+
if new_req.dig(:metadata, :property_name)
|
60
|
+
files = update_files_for_property_change(files, old_req, new_req)
|
61
|
+
elsif new_req.dig(:metadata, :dependency_set)
|
62
|
+
files = update_files_for_dep_set_change(files, old_req, new_req)
|
63
|
+
else
|
64
|
+
files[files.index(buildfile)] =
|
65
|
+
update_version_in_buildfile(
|
66
|
+
dependency,
|
67
|
+
buildfile,
|
68
|
+
old_req,
|
69
|
+
new_req
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
files
|
75
|
+
end
|
76
|
+
|
77
|
+
def update_files_for_property_change(buildfiles, old_req, new_req)
|
78
|
+
files = buildfiles.dup
|
79
|
+
property_name = new_req.fetch(:metadata).fetch(:property_name)
|
80
|
+
buildfile = files.find { |f| f.name == new_req.fetch(:file) }
|
81
|
+
|
82
|
+
PropertyValueUpdater.new(dependency_files: files).
|
83
|
+
update_files_for_property_change(
|
84
|
+
property_name: property_name,
|
85
|
+
callsite_buildfile: buildfile,
|
86
|
+
previous_value: old_req.fetch(:requirement),
|
87
|
+
updated_value: new_req.fetch(:requirement)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def update_files_for_dep_set_change(buildfiles, old_req, new_req)
|
92
|
+
files = buildfiles.dup
|
93
|
+
dependency_set = new_req.fetch(:metadata).fetch(:dependency_set)
|
94
|
+
buildfile = files.find { |f| f.name == new_req.fetch(:file) }
|
95
|
+
|
96
|
+
DependencySetUpdater.new(dependency_files: files).
|
97
|
+
update_files_for_dep_set_change(
|
98
|
+
dependency_set: dependency_set,
|
99
|
+
buildfile: buildfile,
|
100
|
+
previous_requirement: old_req.fetch(:requirement),
|
101
|
+
updated_requirement: new_req.fetch(:requirement)
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def update_version_in_buildfile(dependency, buildfile, previous_req,
|
106
|
+
requirement)
|
107
|
+
updated_content =
|
108
|
+
buildfile.content.gsub(
|
109
|
+
original_buildfile_declaration(dependency, previous_req),
|
110
|
+
updated_buildfile_declaration(
|
111
|
+
dependency,
|
112
|
+
previous_req,
|
113
|
+
requirement
|
114
|
+
)
|
115
|
+
)
|
116
|
+
|
117
|
+
if updated_content == buildfile.content
|
118
|
+
raise "Expected content to change!"
|
119
|
+
end
|
120
|
+
|
121
|
+
updated_file(file: buildfile, content: updated_content)
|
122
|
+
end
|
123
|
+
|
124
|
+
def original_buildfile_declaration(dependency, requirement)
|
125
|
+
# This implementation is limited to declarations that appear on a
|
126
|
+
# single line.
|
127
|
+
buildfile = buildfiles.find { |f| f.name == requirement.fetch(:file) }
|
128
|
+
buildfile.content.lines.find do |line|
|
129
|
+
line = evaluate_properties(line, buildfile)
|
130
|
+
next false unless line.include?(dependency.name.split(":").first)
|
131
|
+
next false unless line.include?(dependency.name.split(":").last)
|
132
|
+
|
133
|
+
line.include?(requirement.fetch(:requirement))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def evaluate_properties(string, buildfile)
|
138
|
+
result = string.dup
|
139
|
+
|
140
|
+
string.scan(Gradle::FileParser::PROPERTY_REGEX) do
|
141
|
+
prop_name = Regexp.last_match.named_captures.fetch("property_name")
|
142
|
+
property_value = property_value_finder.property_value(
|
143
|
+
property_name: prop_name,
|
144
|
+
callsite_buildfile: buildfile
|
145
|
+
)
|
146
|
+
next unless property_value
|
147
|
+
|
148
|
+
result.sub!(Regexp.last_match.to_s, property_value)
|
149
|
+
end
|
150
|
+
|
151
|
+
result
|
152
|
+
end
|
153
|
+
|
154
|
+
def property_value_finder
|
155
|
+
@property_value_finder ||=
|
156
|
+
Gradle::FileParser::PropertyValueFinder.
|
157
|
+
new(dependency_files: dependency_files)
|
158
|
+
end
|
159
|
+
|
160
|
+
def updated_buildfile_declaration(dependency, previous_req, requirement)
|
161
|
+
original_req_string = previous_req.fetch(:requirement)
|
162
|
+
|
163
|
+
original_buildfile_declaration(dependency, previous_req).gsub(
|
164
|
+
original_req_string,
|
165
|
+
requirement.fetch(:requirement)
|
166
|
+
)
|
167
|
+
end
|
168
|
+
|
169
|
+
def buildfiles
|
170
|
+
@buildfiles ||=
|
171
|
+
dependency_files.select { |f| f.name.end_with?("build.gradle") }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
Dependabot::FileUpdaters.register("gradle", Dependabot::Gradle::FileUpdater)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/gradle/file_parser"
|
4
|
+
require "dependabot/gradle/file_updater"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module Gradle
|
8
|
+
class FileUpdater
|
9
|
+
class DependencySetUpdater
|
10
|
+
def initialize(dependency_files:)
|
11
|
+
@dependency_files = dependency_files
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_files_for_dep_set_change(dependency_set:,
|
15
|
+
buildfile:,
|
16
|
+
previous_requirement:,
|
17
|
+
updated_requirement:)
|
18
|
+
declaration_string =
|
19
|
+
original_declaration_string(dependency_set, buildfile)
|
20
|
+
|
21
|
+
return dependency_files unless declaration_string
|
22
|
+
|
23
|
+
updated_content = buildfile.content.sub(
|
24
|
+
declaration_string,
|
25
|
+
declaration_string.sub(
|
26
|
+
previous_requirement,
|
27
|
+
updated_requirement
|
28
|
+
)
|
29
|
+
)
|
30
|
+
|
31
|
+
updated_files = dependency_files.dup
|
32
|
+
updated_files[updated_files.index(buildfile)] =
|
33
|
+
update_file(file: buildfile, content: updated_content)
|
34
|
+
|
35
|
+
updated_files
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :dependency_files
|
41
|
+
|
42
|
+
def original_declaration_string(dependency_set, buildfile)
|
43
|
+
regex = Gradle::FileParser::DEPENDENCY_SET_DECLARATION_REGEX
|
44
|
+
dependency_sets = []
|
45
|
+
buildfile.content.scan(regex) do
|
46
|
+
dependency_sets << Regexp.last_match.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
dependency_sets.find do |mtch|
|
50
|
+
next unless mtch.include?(dependency_set[:group])
|
51
|
+
|
52
|
+
mtch.include?(dependency_set[:version])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_file(file:, content:)
|
57
|
+
updated_file = file.dup
|
58
|
+
updated_file.content = content
|
59
|
+
updated_file
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dependabot/gradle/file_updater"
|
4
|
+
require "dependabot/gradle/file_parser/property_value_finder"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module Gradle
|
8
|
+
class FileUpdater
|
9
|
+
class PropertyValueUpdater
|
10
|
+
def initialize(dependency_files:)
|
11
|
+
@dependency_files = dependency_files
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_files_for_property_change(property_name:,
|
15
|
+
callsite_buildfile:,
|
16
|
+
previous_value:,
|
17
|
+
updated_value:)
|
18
|
+
declaration_details = property_value_finder.property_details(
|
19
|
+
property_name: property_name,
|
20
|
+
callsite_buildfile: callsite_buildfile
|
21
|
+
)
|
22
|
+
declaration_string = declaration_details.fetch(:declaration_string)
|
23
|
+
filename = declaration_details.fetch(:file)
|
24
|
+
|
25
|
+
file_to_update = dependency_files.find { |f| f.name == filename }
|
26
|
+
updated_content = file_to_update.content.sub(
|
27
|
+
declaration_string,
|
28
|
+
declaration_string.sub(previous_value, updated_value)
|
29
|
+
)
|
30
|
+
|
31
|
+
updated_files = dependency_files.dup
|
32
|
+
updated_files[updated_files.index(file_to_update)] =
|
33
|
+
update_file(file: file_to_update, content: updated_content)
|
34
|
+
|
35
|
+
updated_files
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :dependency_files
|
41
|
+
|
42
|
+
def property_value_finder
|
43
|
+
@property_value_finder ||=
|
44
|
+
Gradle::FileParser::PropertyValueFinder.
|
45
|
+
new(dependency_files: dependency_files)
|
46
|
+
end
|
47
|
+
|
48
|
+
def update_file(file:, content:)
|
49
|
+
updated_file = file.dup
|
50
|
+
updated_file.content = content
|
51
|
+
updated_file
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
require "dependabot/metadata_finders/base"
|
5
|
+
require "dependabot/file_fetchers/base"
|
6
|
+
require "dependabot/gradle/file_parser/repositories_finder"
|
7
|
+
|
8
|
+
module Dependabot
|
9
|
+
module Gradle
|
10
|
+
class MetadataFinder < Dependabot::MetadataFinders::Base
|
11
|
+
DOT_SEPARATOR_REGEX = %r{\.(?:(?!\d+[.\/])+)}.freeze
|
12
|
+
PROPERTY_REGEX = /\$\{(?<property>.*?)\}/.freeze
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def look_up_source
|
17
|
+
tmp_source = look_up_source_in_pom(dependency_pom_file)
|
18
|
+
return tmp_source if tmp_source
|
19
|
+
|
20
|
+
return unless (parent = parent_pom_file(dependency_pom_file))
|
21
|
+
|
22
|
+
tmp_source = look_up_source_in_pom(parent)
|
23
|
+
return unless tmp_source
|
24
|
+
|
25
|
+
artifact = dependency.name.split(":").last
|
26
|
+
return tmp_source if tmp_source.repo.end_with?(artifact)
|
27
|
+
return tmp_source if repo_has_subdir_for_dep?(tmp_source)
|
28
|
+
end
|
29
|
+
|
30
|
+
def repo_has_subdir_for_dep?(tmp_source)
|
31
|
+
@repo_has_subdir_for_dep ||= {}
|
32
|
+
if @repo_has_subdir_for_dep.key?(tmp_source)
|
33
|
+
return @repo_has_subdir_for_dep[tmp_source]
|
34
|
+
end
|
35
|
+
|
36
|
+
artifact = dependency.name.split(":").last
|
37
|
+
fetcher =
|
38
|
+
FileFetchers::Base.new(source: tmp_source, credentials: credentials)
|
39
|
+
|
40
|
+
@repo_has_subdir_for_dep[tmp_source] =
|
41
|
+
fetcher.send(:repo_contents, raise_errors: false).
|
42
|
+
select { |f| f.type == "dir" }.
|
43
|
+
any? { |f| artifact.end_with?(f.name) }
|
44
|
+
rescue Dependabot::RepoNotFound
|
45
|
+
@repo_has_subdir_for_dep[tmp_source] = false
|
46
|
+
end
|
47
|
+
|
48
|
+
def look_up_source_in_pom(pom)
|
49
|
+
potential_source_urls = [
|
50
|
+
pom.at_css("project > url")&.content,
|
51
|
+
pom.at_css("project > scm > url")&.content,
|
52
|
+
pom.at_css("project > issueManagement > url")&.content
|
53
|
+
].compact
|
54
|
+
|
55
|
+
source_url = potential_source_urls.find { |url| Source.from_url(url) }
|
56
|
+
source_url ||= source_from_anywhere_in_pom(pom)
|
57
|
+
source_url = substitute_property_in_source_url(source_url, pom)
|
58
|
+
|
59
|
+
Source.from_url(source_url)
|
60
|
+
end
|
61
|
+
|
62
|
+
def substitute_property_in_source_url(source_url, pom)
|
63
|
+
return unless source_url
|
64
|
+
return source_url unless source_url.include?("${")
|
65
|
+
|
66
|
+
regex = PROPERTY_REGEX
|
67
|
+
property_name = source_url.match(regex).named_captures["property"]
|
68
|
+
doc = pom.dup
|
69
|
+
doc.remove_namespaces!
|
70
|
+
nm = property_name.sub(/^pom\./, "").sub(/^project\./, "")
|
71
|
+
property_value =
|
72
|
+
loop do
|
73
|
+
candidate_node =
|
74
|
+
doc.at_xpath("/project/#{nm}") ||
|
75
|
+
doc.at_xpath("/project/properties/#{nm}") ||
|
76
|
+
doc.at_xpath("/project/profiles/profile/properties/#{nm}")
|
77
|
+
break candidate_node.content if candidate_node
|
78
|
+
break unless nm.match?(DOT_SEPARATOR_REGEX)
|
79
|
+
|
80
|
+
nm = nm.sub(DOT_SEPARATOR_REGEX, "/")
|
81
|
+
end
|
82
|
+
|
83
|
+
source_url.gsub("${#{property_name}}", property_value)
|
84
|
+
end
|
85
|
+
|
86
|
+
def source_from_anywhere_in_pom(pom)
|
87
|
+
github_urls = []
|
88
|
+
pom.to_s.scan(Source::SOURCE_REGEX) do
|
89
|
+
github_urls << Regexp.last_match.to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
github_urls.find do |url|
|
93
|
+
repo = Source.from_url(url).repo
|
94
|
+
repo.end_with?(dependency.name.split(":").last)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def dependency_pom_file
|
99
|
+
return @dependency_pom_file unless @dependency_pom_file.nil?
|
100
|
+
|
101
|
+
artifact_id = dependency.name.split(":").last
|
102
|
+
response = Excon.get(
|
103
|
+
"#{maven_repo_dependency_url}/"\
|
104
|
+
"#{dependency.version}/"\
|
105
|
+
"#{artifact_id}-#{dependency.version}.pom",
|
106
|
+
headers: auth_details,
|
107
|
+
idempotent: true,
|
108
|
+
**SharedHelpers.excon_defaults
|
109
|
+
)
|
110
|
+
|
111
|
+
@dependency_pom_file = Nokogiri::XML(response.body)
|
112
|
+
rescue Excon::Error::Timeout
|
113
|
+
@dependency_pom_file = Nokogiri::XML("")
|
114
|
+
end
|
115
|
+
|
116
|
+
def parent_pom_file(pom)
|
117
|
+
doc = pom.dup
|
118
|
+
doc.remove_namespaces!
|
119
|
+
group_id = doc.at_xpath("/project/parent/groupId")&.content&.strip
|
120
|
+
artifact_id =
|
121
|
+
doc.at_xpath("/project/parent/artifactId")&.content&.strip
|
122
|
+
version = doc.at_xpath("/project/parent/version")&.content&.strip
|
123
|
+
|
124
|
+
return unless artifact_id && group_id && version
|
125
|
+
|
126
|
+
response = Excon.get(
|
127
|
+
"#{maven_repo_url}/#{group_id.tr('.', '/')}/#{artifact_id}/"\
|
128
|
+
"#{version}/"\
|
129
|
+
"#{artifact_id}-#{version}.pom",
|
130
|
+
headers: auth_details,
|
131
|
+
idempotent: true,
|
132
|
+
**SharedHelpers.excon_defaults
|
133
|
+
)
|
134
|
+
|
135
|
+
Nokogiri::XML(response.body)
|
136
|
+
end
|
137
|
+
|
138
|
+
def maven_repo_url
|
139
|
+
source = dependency.requirements.
|
140
|
+
find { |r| r&.fetch(:source) }&.fetch(:source)
|
141
|
+
|
142
|
+
source&.fetch(:url, nil) ||
|
143
|
+
source&.fetch("url") ||
|
144
|
+
Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL
|
145
|
+
end
|
146
|
+
|
147
|
+
def maven_repo_dependency_url
|
148
|
+
group_id, artifact_id = dependency.name.split(":")
|
149
|
+
|
150
|
+
"#{maven_repo_url}/#{group_id.tr('.', '/')}/#{artifact_id}"
|
151
|
+
end
|
152
|
+
|
153
|
+
def auth_details
|
154
|
+
cred =
|
155
|
+
credentials.select { |c| c["type"] == "maven_repository" }.
|
156
|
+
find do |c|
|
157
|
+
cred_url = c.fetch("url").gsub(%r{/+$}, "")
|
158
|
+
next false unless cred_url == maven_repo_url
|
159
|
+
|
160
|
+
c.fetch("username", nil)
|
161
|
+
end
|
162
|
+
|
163
|
+
return {} unless cred
|
164
|
+
|
165
|
+
token = cred.fetch("username") + ":" + cred.fetch("password")
|
166
|
+
encoded_token = Base64.encode64(token).delete("\n")
|
167
|
+
{ "Authorization" => "Basic #{encoded_token}" }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
Dependabot::MetadataFinders.
|
174
|
+
register("gradle", Dependabot::Gradle::MetadataFinder)
|