Skip to content

Commit 34a5f2a

Browse files
Implement sbt metadata finder (#15011)
* handle pubspec validation errors gracefully * implement sbt metadata finder * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * revert incorrect pub file changes * fix sorbet error --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent a76096e commit 34a5f2a

9 files changed

Lines changed: 1985 additions & 157 deletions

File tree

gradle/lib/dependabot/gradle/metadata_finder.rb

Lines changed: 25 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
# typed: strict
22
# frozen_string_literal: true
33

4-
require "nokogiri"
54
require "sorbet-runtime"
65

7-
require "dependabot/file_fetchers/base"
86
require "dependabot/gradle/distributions"
97
require "dependabot/gradle/file_fetcher"
108
require "dependabot/gradle/file_parser/repositories_finder"
11-
require "dependabot/maven/utils/auth_headers_finder"
9+
require "dependabot/maven/shared/shared_metadata_finder"
1210
require "dependabot/metadata_finders"
13-
require "dependabot/metadata_finders/base"
14-
require "dependabot/registry_client"
1511

1612
module Dependabot
1713
module Gradle
18-
class MetadataFinder < Dependabot::MetadataFinders::Base
14+
class MetadataFinder < Dependabot::Maven::Shared::SharedMetadataFinder
1915
extend T::Sig
2016

21-
DOT_SEPARATOR_REGEX = %r{\.(?!\d+([.\/_\-]|$)+)}
22-
PROPERTY_REGEX = /\$\{(?<property>.*?)\}/
2317
KOTLIN_PLUGIN_REPO_PREFIX = "org.jetbrains.kotlin"
2418

2519
private
@@ -28,18 +22,7 @@ class MetadataFinder < Dependabot::MetadataFinders::Base
2822
def look_up_source
2923
return distributions_source if Distributions.distribution_requirements?(dependency.requirements)
3024

31-
tmp_source = look_up_source_in_pom(dependency_pom_file)
32-
return tmp_source if tmp_source
33-
34-
return unless (parent = parent_pom_file(dependency_pom_file))
35-
36-
tmp_source = look_up_source_in_pom(parent)
37-
return unless tmp_source
38-
39-
artifact = dependency.name.split(":").last
40-
return tmp_source if tmp_source.repo.end_with?(T.must(artifact))
41-
42-
tmp_source if repo_has_subdir_for_dep?(tmp_source)
25+
super
4326
end
4427

4528
# The Gradle Wrapper does not have its own release notes.
@@ -53,120 +36,35 @@ def distributions_source
5336
)
5437
end
5538

56-
sig { params(tmp_source: Dependabot::Source).returns(T::Boolean) }
57-
def repo_has_subdir_for_dep?(tmp_source)
58-
@repo_has_subdir_for_dep ||= T.let({}, T.nilable(T::Hash[Dependabot::Source, T::Boolean]))
59-
return T.must(@repo_has_subdir_for_dep[tmp_source]) if @repo_has_subdir_for_dep.key?(tmp_source)
60-
61-
artifact = dependency.name.split(":").last
62-
fetcher =
63-
Dependabot::Gradle::FileFetcher.new(source: tmp_source, credentials: credentials)
64-
65-
@repo_has_subdir_for_dep[tmp_source] =
66-
fetcher.send(:repo_contents, raise_errors: false)
67-
.select { |f| f.type == "dir" }
68-
.any? { |f| artifact&.end_with?(f.name) }
69-
rescue Dependabot::BranchNotFound
70-
tmp_source.branch = nil
71-
retry
72-
rescue Dependabot::RepoNotFound
73-
T.must(@repo_has_subdir_for_dep)[tmp_source] = false
74-
end
75-
76-
sig { params(pom: Nokogiri::XML::Document).returns(T.nilable(Dependabot::Source)) }
77-
def look_up_source_in_pom(pom)
78-
potential_source_urls = [
79-
pom.at_css("project > url")&.content,
80-
pom.at_css("project > scm > url")&.content,
81-
pom.at_css("project > issueManagement > url")&.content
82-
].compact
83-
84-
source_url = potential_source_urls.find { |url| Source.from_url(url) }
85-
source_url ||= source_from_anywhere_in_pom(pom)
86-
source_url = substitute_property_in_source_url(source_url, pom)
87-
88-
Source.from_url(source_url)
89-
end
90-
91-
sig { params(source_url: T.nilable(String), pom: Nokogiri::XML::Document).returns(T.nilable(String)) }
92-
def substitute_property_in_source_url(source_url, pom)
93-
return unless source_url
94-
return source_url unless source_url.include?("${")
95-
96-
regex = PROPERTY_REGEX
97-
property_name = T.must(source_url.match(regex)).named_captures["property"]
98-
doc = pom.dup
99-
doc.remove_namespaces!
100-
nm = T.must(property_name).sub(/^pom\./, "").sub(/^project\./, "")
101-
property_value =
102-
loop do
103-
candidate_node =
104-
doc.at_xpath("/project/#{nm}") ||
105-
doc.at_xpath("/project/properties/#{nm}") ||
106-
doc.at_xpath("/project/profiles/profile/properties/#{nm}")
107-
break(candidate_node.content) if candidate_node
108-
break unless nm.match?(DOT_SEPARATOR_REGEX)
109-
110-
nm = nm.sub(DOT_SEPARATOR_REGEX, "/")
111-
end
112-
113-
source_url.gsub("${#{property_name}}", property_value)
39+
sig { override.returns(T.class_of(Dependabot::FileFetchers::Base)) }
40+
def file_fetcher_class
41+
Dependabot::Gradle::FileFetcher
11442
end
11543

116-
sig { params(pom: T.any(String, Nokogiri::XML::Document)).returns(T.nilable(String)) }
117-
def source_from_anywhere_in_pom(pom)
118-
github_urls = []
119-
pom.to_s.scan(Source::SOURCE_REGEX) do
120-
github_urls << Regexp.last_match.to_s
121-
end
122-
123-
github_urls.find do |url|
124-
repo = T.must(Source.from_url(url)).repo
125-
repo.end_with?(T.must(dependency.name.split(":").last))
44+
sig { override.returns(T.nilable(String)) }
45+
def dependency_artifact_id
46+
if kotlin_plugin? then "#{KOTLIN_PLUGIN_REPO_PREFIX}.#{dependency.name}.gradle.plugin"
47+
elsif plugin? then "#{dependency.name}.gradle.plugin"
48+
else
49+
dependency.name.split(":").last
12650
end
12751
end
12852

129-
sig { returns(Nokogiri::XML::Document) }
130-
def dependency_pom_file
131-
return @dependency_pom_file unless @dependency_pom_file.nil?
132-
133-
artifact_id =
134-
if kotlin_plugin? then "#{KOTLIN_PLUGIN_REPO_PREFIX}.#{dependency.name}.gradle.plugin"
135-
elsif plugin? then "#{dependency.name}.gradle.plugin"
53+
sig { override.returns(String) }
54+
def maven_repo_dependency_url
55+
group_id, artifact_id =
56+
if kotlin_plugin?
57+
["#{KOTLIN_PLUGIN_REPO_PREFIX}.#{dependency.name}",
58+
"#{KOTLIN_PLUGIN_REPO_PREFIX}.#{dependency.name}.gradle.plugin"]
59+
elsif plugin? then [dependency.name, "#{dependency.name}.gradle.plugin"]
13660
else
137-
dependency.name.split(":").last
61+
dependency.name.split(":")
13862
end
13963

140-
response = Dependabot::RegistryClient.get(
141-
url: "#{maven_repo_dependency_url}/#{dependency.version}/#{artifact_id}-#{dependency.version}.pom",
142-
headers: auth_headers
143-
)
144-
145-
@dependency_pom_file = T.let(Nokogiri::XML(response.body), T.nilable(Nokogiri::XML::Document))
146-
rescue Excon::Error::Timeout
147-
@dependency_pom_file ||= T.let(Nokogiri::XML(""), T.nilable(Nokogiri::XML::Document))
148-
end
149-
150-
sig { params(pom: Nokogiri::XML::Document).returns(T.nilable(Nokogiri::XML::Document)) }
151-
def parent_pom_file(pom)
152-
doc = pom.dup
153-
doc.remove_namespaces!
154-
group_id = doc.at_xpath("/project/parent/groupId")&.content&.strip
155-
artifact_id =
156-
doc.at_xpath("/project/parent/artifactId")&.content&.strip
157-
version = doc.at_xpath("/project/parent/version")&.content&.strip
158-
159-
return unless artifact_id && group_id && version
160-
161-
response = Dependabot::RegistryClient.get(
162-
url: "#{maven_repo_url}/#{group_id.tr('.', '/')}/#{artifact_id}/#{version}/#{artifact_id}-#{version}.pom",
163-
headers: auth_headers
164-
)
165-
166-
Nokogiri::XML(response.body)
64+
"#{maven_repo_url}/#{group_id&.tr('.', '/')}/#{artifact_id}"
16765
end
16866

169-
sig { returns(String) }
67+
sig { override.returns(String) }
17068
def maven_repo_url
17169
source = dependency.requirements
17270
.find { |r| r.fetch(:source) }&.fetch(:source)
@@ -176,18 +74,9 @@ def maven_repo_url
17674
Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL
17775
end
17876

179-
sig { returns(String) }
180-
def maven_repo_dependency_url
181-
group_id, artifact_id =
182-
if kotlin_plugin?
183-
["#{KOTLIN_PLUGIN_REPO_PREFIX}.#{dependency.name}",
184-
"#{KOTLIN_PLUGIN_REPO_PREFIX}.#{dependency.name}.gradle.plugin"]
185-
elsif plugin? then [dependency.name, "#{dependency.name}.gradle.plugin"]
186-
else
187-
dependency.name.split(":")
188-
end
189-
190-
"#{maven_repo_url}/#{group_id&.tr('.', '/')}/#{artifact_id}"
77+
sig { override.returns(String) }
78+
def central_repo_url
79+
Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL
19180
end
19281

19382
sig { returns(T::Boolean) }
@@ -199,14 +88,6 @@ def plugin?
19988
def kotlin_plugin?
20089
plugin? && dependency.requirements.any? { |r| r.fetch(:groups).include? "kotlin" }
20190
end
202-
203-
sig { returns(T::Hash[String, String]) }
204-
def auth_headers
205-
@auth_headers ||= T.let(
206-
Dependabot::Maven::Utils::AuthHeadersFinder.new(credentials).auth_headers(maven_repo_url),
207-
T.nilable(T::Hash[String, String])
208-
)
209-
end
21091
end
21192
end
21293
end

maven/lib/dependabot/maven/shared/shared_metadata_finder.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def dependency_pom_file
140140
@dependency_pom_file = Nokogiri::XML("")
141141
end
142142

143-
sig { returns(T.nilable(String)) }
143+
sig { overridable.returns(T.nilable(String)) }
144144
def dependency_artifact_id
145145
_group_id, artifact_id = dependency.name.split(":")
146146
artifact_id
@@ -186,7 +186,7 @@ def central_repo_url
186186
).central_repo_url
187187
end
188188

189-
sig { returns(String) }
189+
sig { overridable.returns(String) }
190190
def maven_repo_dependency_url
191191
group_id, artifact_id = dependency.name.split(":")
192192
"#{maven_repo_url}/#{T.must(group_id).tr('.', '/')}/#{artifact_id}"

sbt/lib/dependabot/sbt/metadata_finder.rb

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
# typed: strong
22
# frozen_string_literal: true
33

4-
# NOTE: This file was scaffolded automatically but is OPTIONAL.
5-
# If you don't need custom metadata finding logic (changelogs, release notes, etc.),
6-
# you can safely delete this file and remove the require from lib/dependabot/sbt.rb
7-
84
require "dependabot/metadata_finders"
9-
require "dependabot/metadata_finders/base"
5+
require "dependabot/maven/shared/shared_metadata_finder"
6+
require "dependabot/sbt/file_fetcher"
7+
require "sorbet-runtime"
108

119
module Dependabot
1210
module Sbt
13-
class MetadataFinder < Dependabot::MetadataFinders::Base
11+
class MetadataFinder < Dependabot::Maven::Shared::SharedMetadataFinder
1412
extend T::Sig
1513

1614
private
1715

18-
sig { override.returns(T.nilable(Dependabot::Source)) }
19-
def look_up_source
20-
# TODO: Implement custom source lookup logic if needed
21-
# Otherwise, delete this file and the require in the main registration file
22-
nil
16+
sig { override.returns(T.class_of(Dependabot::FileFetchers::Base)) }
17+
def file_fetcher_class
18+
Dependabot::Sbt::FileFetcher
2319
end
2420
end
2521
end

0 commit comments

Comments
 (0)