Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 60 additions & 21 deletions gradle/lib/dependabot/gradle/package/package_details_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,38 +102,67 @@ def fetch_available_versions
sig { returns(T::Hash[String, T::Hash[Symbol, T.untyped]]) }
def release_details
release_date_info = T.let({}, T::Hash[String, T::Hash[Symbol, T.untyped]])

begin
repositories.map do |repository_details|
repositories.each do |repository_details|
url = repository_details.fetch("url")
next unless url == Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL

release_info_metadata(repository_details).css("a[title]").each do |link|
version_string = link["title"]
version = version_string.gsub(%r{/$}, "")
raw_date_text = link.next.text.strip.split("\n").last.strip

release_date = begin
Time.parse(raw_date_text)
rescue StandardError
nil
end

next unless version && version_class.correct?(version)

release_date_info[version] = {
release_date: release_date
}
if url == Gradle::FileParser::RepositoriesFinder::CENTRAL_REPO_URL
parse_maven_central_releases(repository_details, release_date_info)
elsif url == Gradle::FileParser::RepositoriesFinder::GRADLE_PLUGINS_REPO
parse_gradle_plugin_portal_release(repository_details, release_date_info)
end
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated
end
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated

release_date_info
rescue StandardError
Dependabot.logger.error("Failed to get release date")
rescue StandardError => e
Dependabot.logger.error("Failed to get release date: #{e.class} - #{e.message}")
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated
Dependabot.logger.error(e.backtrace&.join("\n") || "No backtrace available")
{}
end
end

sig do
params(
repository_details: T::Hash[String, T.untyped],
release_date_info: T::Hash[String, T::Hash[Symbol, T.untyped]]
).void
end
def parse_maven_central_releases(repository_details, release_date_info)
release_info_metadata(repository_details).css("a[title]").each do |link|
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated
version = link["title"].gsub(%r{/$}, "")
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated
raw_date_text = link.next.text.strip.split("\n").last.strip

release_date = begin
Time.parse(raw_date_text)
rescue StandardError
nil
end

next unless version && version_class.correct?(version)

release_date_info[version] = {
release_date: release_date
}
end
end

sig do
params(
repository_details: T::Hash[String, T.untyped],
release_date_info: T::Hash[String, T::Hash[Symbol, T.untyped]]
).void
end
def parse_gradle_plugin_portal_release(repository_details, release_date_info)
metadata_xml = dependency_metadata(repository_details)
last_updated = metadata_xml.at_xpath("//metadata/versioning/lastUpdated")&.text&.strip
release_date = parse_gradle_timestamp(last_updated)
latest_version = metadata_xml.at_xpath("//metadata/versioning/latest")&.text&.strip
return unless latest_version && version_class.correct?(latest_version)

Dependabot.logger.info("Parsed Gradle Plugin Portal release: #{latest_version} at #{release_date}")
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated
release_date_info[latest_version] = { release_date: release_date }
end

sig { returns(T::Array[T::Hash[String, T.untyped]]) }
def repositories
return @repositories if @repositories
Expand Down Expand Up @@ -383,6 +412,16 @@ def central_repo_urls
%w(http:// https://).map { |p| p + central_url_without_protocol }
end

sig { params(timestamp: T.nilable(String)).returns(T.nilable(Time)) }
def parse_gradle_timestamp(timestamp)
return nil if timestamp.nil? || timestamp.empty?
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated

Time.strptime(timestamp, "%Y%m%d%H%M%S") # Parse YYYYMMDDHHmmss format
rescue ArgumentError => e
Dependabot.logger.warn("Failed to parse Gradle timestamp '#{timestamp}': #{e.message}")
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated
nil
Comment thread
thavaahariharangit marked this conversation as resolved.
Outdated
end

sig { returns(T.class_of(Dependabot::Version)) }
def version_class
dependency.version_class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,24 @@
"https://repo.maven.apache.org/maven2/org/springframework/boot/" \
"org.springframework.boot.gradle.plugin/maven-metadata.xml"
end
let(:maven_central_html_url) do
"https://repo.maven.apache.org/maven2/org/springframework/boot/" \
"org.springframework.boot.gradle.plugin/"
end

before do
stub_request(:get, gradle_plugin_metadata_url)
.to_return(status: 200, body: gradle_plugin_releases)
stub_request(:get, maven_metadata_url).to_return(status: 404)
# Stub the HTML directory listing request for Maven Central
stub_request(:get, maven_central_html_url).to_return(status: 404)
end

it "populates release_details for the latest version" do
release_info = packagedetailsfetcher.send(:release_details)
expect(release_info).to be_a(Hash)
expect(release_info).to have_key("2.1.4.RELEASE")
expect(release_info["2.1.4.RELEASE"][:release_date]).to eq(Time.utc(2019, 4, 4, 5, 30, 33))
end

describe "the first version" do
Expand All @@ -135,6 +148,11 @@
its([:source_url]) do
is_expected.to eq("https://plugins.gradle.org/m2")
end

its([:released_at]) do
# lastUpdated from fixture: 20190404053033 (2019-04-04 05:30:33 UTC)
is_expected.to eq(Time.utc(2019, 4, 4, 5, 30, 33))
end
end
end

Expand All @@ -161,11 +179,17 @@
"https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/jvm/" \
"org.jetbrains.kotlin.jvm.gradle.plugin/maven-metadata.xml"
end
let(:maven_central_html_url) do
"https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/jvm/" \
"org.jetbrains.kotlin.jvm.gradle.plugin/"
end

before do
stub_request(:get, gradle_plugin_metadata_url)
.to_return(status: 200, body: gradle_plugin_releases)
stub_request(:get, maven_metadata_url).to_return(status: 404)
# Stub the HTML directory listing request for Maven Central
stub_request(:get, maven_central_html_url).to_return(status: 404)
end

describe "the first version" do
Expand All @@ -190,6 +214,11 @@
its([:source_url]) do
is_expected.to eq("https://plugins.gradle.org/m2")
end

its([:released_at]) do
# lastUpdated from fixture: 20201222143435 (2020-12-22 14:34:35 UTC)
is_expected.to eq(Time.utc(2020, 12, 22, 14, 34, 35))
end
end
end

Expand Down Expand Up @@ -337,4 +366,67 @@
end
end
end

describe "#parse_gradle_timestamp" do
subject(:parse_timestamp) { fetcher.send(:parse_gradle_timestamp, timestamp) }

let(:fetcher) do
described_class.new(
dependency: dependency,
dependency_files: dependency_files,
credentials: credentials,
forbidden_urls: []
)
end
let(:dependency_requirements) do
[{
file: "build.gradle",
requirement: "1.0.0",
groups: ["plugins"],
source: nil
}]
end
let(:dependency_name) { "test.plugin" }
let(:dependency_version) { "1.0.0" }

context "with valid timestamp" do
let(:timestamp) { "20191201191459" }

it "parses YYYYMMDDHHmmss format correctly" do
expect(parse_timestamp).to eq(Time.utc(2019, 12, 1, 19, 14, 59))
end
end

context "with nil timestamp" do
let(:timestamp) { nil }

it "returns nil" do
expect(parse_timestamp).to be_nil
end
end

context "with empty timestamp" do
let(:timestamp) { "" }

it "returns nil" do
expect(parse_timestamp).to be_nil
end
end

context "with invalid timestamp format" do
let(:timestamp) { "invalid" }

it "returns nil" do
expect(parse_timestamp).to be_nil
end
end

context "with non-numeric timestamp" do
let(:timestamp) { "abcd1201191459" }

it "returns nil" do
expect(parse_timestamp).to be_nil
end
end
end
end
Loading