dependabot-gradle 0.377.0 → 0.378.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc9ccb07b414741f2b8839cd0fb9515a004b20df4dfb5b008347af097a93128a
4
- data.tar.gz: d974e7d7f85518016c86c19366421127781be99f2d014b16891d6483d9196e76
3
+ metadata.gz: 0016ba8c19319e359c4d4ba76a14a47d4d1c55f6f5dab5fe72604df215d52777
4
+ data.tar.gz: 746d406e83069c13be3aa0a987273f0d216e8811c7766a023b83ce492a5e9fc3
5
5
  SHA512:
6
- metadata.gz: b9c4b0b21c8b0dd3625b464491a723e63bfb5d12c6524e503f0edae004a65cfa7194c86638c2a8eb609652b79e3625dbbfdebbbe41f620ce889679a1e2aacddf
7
- data.tar.gz: 20b4d157bf6a305cafe771152a638ef3f4290d935cb7b2d221c62338c33e8b18de2bb6b55586d0808118993b34f37d7b62392eb02c9324f0f010a465fa4eed8c
6
+ metadata.gz: 0a5ee60f58ec228d0e2c246798dfa01007472fdf2f76d33fdd4ed09d079dd7b29667a74eb996ffde9201cd723d38c74b4dd96bf60b19856faedfbfca80eda64b
7
+ data.tar.gz: 3849e2484105ca53b3c39ff4d2410f343d000dd0d8106c78dbb320a70867829e2a3653501139a5170b4e104ea7878e86ddf1df8a02afefaa1ab699dc91ab9c10
@@ -11,8 +11,10 @@ require "dependabot/gradle/requirement"
11
11
  require "dependabot/gradle/distributions"
12
12
  require "dependabot/maven/utils/auth_headers_finder"
13
13
  require "sorbet-runtime"
14
+ require "dependabot/logger"
14
15
  require "dependabot/gradle/metadata_finder"
15
16
  require "dependabot/gradle/package/release_date_extractor"
17
+ require "dependabot/gradle/package/version_release_date_fallback_fetcher"
16
18
 
17
19
  module Dependabot
18
20
  module Gradle
@@ -37,10 +39,11 @@ module Dependabot
37
39
  @dependency_files = dependency_files
38
40
  @credentials = credentials
39
41
  @forbidden_urls = forbidden_urls
40
-
41
42
  @repositories = T.let(nil, T.nilable(T::Array[T::Hash[String, T.untyped]]))
42
43
  @google_version_details = T.let(nil, T.nilable(T::Array[T::Hash[String, T.untyped]]))
43
44
  @dependency_repository_details = T.let(nil, T.nilable(T::Array[T::Hash[String, T.untyped]]))
45
+ @release_details = T.let(nil, T.nilable(T::Hash[String, T::Hash[Symbol, T.untyped]]))
46
+ @version_release_date_fallback_fetcher = T.let(nil, T.nilable(VersionReleaseDateFallbackFetcher))
44
47
  end
45
48
 
46
49
  sig { returns(Dependabot::Dependency) }
@@ -56,17 +59,12 @@ module Dependabot
56
59
  attr_reader :forbidden_urls
57
60
 
58
61
  # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
59
- sig do
60
- returns(T::Array[T::Hash[String, T.untyped]])
61
- end
62
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
62
63
  def fetch_available_versions
63
- T.let({}, T::Hash[String, T::Hash[Symbol, T.untyped]])
64
64
  package_releases = T.let([], T::Array[T::Hash[String, T.untyped]])
65
-
66
65
  version_details =
67
66
  repositories.map do |repository_details|
68
67
  url = repository_details.fetch("url")
69
-
70
68
  next distribution_version_details if url == Gradle::Distributions::DISTRIBUTION_REPOSITORY_URL
71
69
  next google_version_details if url == Gradle::FileParser::RepositoriesFinder::GOOGLE_MAVEN_REPO
72
70
 
@@ -80,37 +78,78 @@ module Dependabot
80
78
 
81
79
  version_details = version_details.sort_by { |details| details.fetch(:version) }
82
80
  release_date_info = release_details
83
-
84
- version_details.map do |info|
85
- version = info[:version]&.to_s
86
-
81
+ version_details.each do |info|
87
82
  package_releases << {
88
- version: Gradle::Version.new(version),
89
- released_at: info[:released_at] || release_date_info[version]&.fetch(:release_date),
83
+ version: Gradle::Version.new(info[:version].to_s),
84
+ released_at: info[:released_at] || release_date_info[info[:version].to_s]&.fetch(:release_date),
90
85
  source_url: info[:source_url]
91
86
  }
92
87
  end
93
88
  if version_details.none? && T.must(forbidden_urls).any?
94
- raise PrivateSourceAuthenticationFailure,
95
- T.must(forbidden_urls).first
89
+ raise PrivateSourceAuthenticationFailure, T.must(forbidden_urls).first
96
90
  end
97
- # version_details
98
91
 
99
92
  package_releases
100
93
  end
101
94
  # rubocop:enable Metrics/AbcSize,Metrics/PerceivedComplexity
102
95
 
96
+ sig { params(release: Dependabot::Package::PackageRelease).returns(Dependabot::Package::PackageRelease) }
97
+ def fetch_release_metadata(release:)
98
+ return release if release.released_at
99
+
100
+ release_date = release_details[release.version.to_s]&.fetch(:release_date, nil)
101
+ hydrated_release = build_release_with_date(release, release_date)
102
+
103
+ return hydrated_release if hydrated_release.released_at || !plugin?
104
+
105
+ build_release_with_date(hydrated_release, version_release_date_fallback(release.version.to_s))
106
+ end
107
+
103
108
  sig { returns(T::Hash[String, T::Hash[Symbol, T.untyped]]) }
104
109
  def release_details
105
- extractor = ReleaseDateExtractor.new(
106
- dependency_name: dependency.name,
107
- version_class: version_class
108
- )
110
+ @release_details ||= begin
111
+ extractor = ReleaseDateExtractor.new(dependency_name: dependency.name, version_class: version_class)
112
+ extractor.extract(
113
+ repositories: repositories,
114
+ dependency_metadata_fetcher: ->(repo) { dependency_metadata(repo) },
115
+ release_info_metadata_fetcher: ->(repo) { release_info_metadata(repo) }
116
+ )
117
+ end
118
+ end
109
119
 
110
- extractor.extract(
111
- repositories: repositories,
112
- dependency_metadata_fetcher: ->(repo) { dependency_metadata(repo) },
113
- release_info_metadata_fetcher: ->(repo) { release_info_metadata(repo) }
120
+ sig { params(version: String).returns(T.nilable(Time)) }
121
+ def version_release_date_fallback(version)
122
+ version_release_date_fallback_fetcher.fetch(version)
123
+ end
124
+
125
+ sig { params(repository_url: String, version: String).returns(String) }
126
+ def plugin_version_pom_url(repository_url, version)
127
+ group_id, artifact_id = group_and_artifact_ids
128
+ group_id = "#{Dependabot::Gradle::MetadataFinder::KOTLIN_PLUGIN_REPO_PREFIX}.#{group_id}" if kotlin_plugin?
129
+ pom_filename = "#{artifact_id}-#{version}.pom"
130
+ File.join(repository_url, T.must(group_id).tr(".", "/"), artifact_id, version, pom_filename)
131
+ end
132
+
133
+ sig do
134
+ params(
135
+ release: Dependabot::Package::PackageRelease,
136
+ release_date: T.nilable(Time)
137
+ )
138
+ .returns(Dependabot::Package::PackageRelease)
139
+ end
140
+ def build_release_with_date(release, release_date)
141
+ Dependabot::Package::PackageRelease.new(
142
+ version: release.version,
143
+ released_at: release_date,
144
+ latest: release.latest,
145
+ yanked: release.yanked,
146
+ yanked_reason: release.yanked_reason,
147
+ downloads: release.downloads,
148
+ url: release.url,
149
+ package_type: release.package_type,
150
+ language: release.language,
151
+ tag: release.tag,
152
+ details: release.details
114
153
  )
115
154
  end
116
155
 
@@ -130,7 +169,6 @@ module Dependabot
130
169
  details.reject do |repo|
131
170
  next if repo["auth_headers"]
132
171
 
133
- # Reject this entry if an identical one with non-empty auth_headers exists
134
172
  details.any? { |r| r["url"] == repo["url"] && r["auth_headers"] != {} }
135
173
  end
136
174
  end
@@ -204,8 +242,6 @@ module Dependabot
204
242
  end
205
243
  end
206
244
 
207
- # Fetches HTML directory listing from Maven-compatible repositories.
208
- # Uses CSS selector "a[title]" to extract versions and dates. Caches results per repository.
209
245
  sig { params(repository_details: T::Hash[T.untyped, T.untyped]).returns(T.untyped) }
210
246
  def release_info_metadata(repository_details)
211
247
  @release_info_metadata ||= T.let({}, T.nilable(T::Hash[Integer, T.untyped]))
@@ -276,18 +312,13 @@ module Dependabot
276
312
 
277
313
  sig { returns(T::Array[T::Hash[String, String]]) }
278
314
  def plugin_repository_details
279
- [{
280
- "url" => Gradle::FileParser::RepositoriesFinder::GRADLE_PLUGINS_REPO,
281
- "auth_headers" => {}
282
- }] + dependency_repository_details
315
+ [{ "url" => Gradle::FileParser::RepositoriesFinder::GRADLE_PLUGINS_REPO, "auth_headers" => {} }] +
316
+ dependency_repository_details
283
317
  end
284
318
 
285
319
  sig { returns(T::Array[T::Hash[String, T.untyped]]) }
286
320
  def distribution_repository_details
287
- [{
288
- "url" => Gradle::Distributions::DISTRIBUTION_REPOSITORY_URL,
289
- "auth_headers" => {}
290
- }]
321
+ [{ "url" => Gradle::Distributions::DISTRIBUTION_REPOSITORY_URL, "auth_headers" => {} }]
291
322
  end
292
323
 
293
324
  sig { params(comparison_version: T.untyped).returns(T::Boolean) }
@@ -355,6 +386,16 @@ module Dependabot
355
386
  Distributions.distribution_requirements?(dependency.requirements)
356
387
  end
357
388
 
389
+ sig { returns(VersionReleaseDateFallbackFetcher) }
390
+ def version_release_date_fallback_fetcher
391
+ @version_release_date_fallback_fetcher ||= VersionReleaseDateFallbackFetcher.new(
392
+ dependency_name: dependency.name,
393
+ repositories: repositories,
394
+ forbidden_urls: T.must(forbidden_urls),
395
+ pom_url_builder: ->(repository_url, version) { plugin_version_pom_url(repository_url, version) }
396
+ )
397
+ end
398
+
358
399
  sig { returns(T::Array[String]) }
359
400
  def central_repo_urls
360
401
  central_url_without_protocol =
@@ -45,19 +45,12 @@ module Dependabot
45
45
  release_date_info = T.let({}, T::Hash[String, T::Hash[Symbol, T.untyped]])
46
46
 
47
47
  begin
48
- repositories.each do |repository_details|
49
- parse_gradle_plugin_portal_release(
50
- repository_details,
51
- release_date_info,
52
- dependency_metadata_fetcher
53
- )
54
-
55
- parse_maven_central_releases(
56
- repository_details,
57
- release_date_info,
58
- release_info_metadata_fetcher
59
- )
60
- end
48
+ parse_repository_release_dates(
49
+ repositories: repositories,
50
+ release_date_info: release_date_info,
51
+ dependency_metadata_fetcher: dependency_metadata_fetcher,
52
+ release_info_metadata_fetcher: release_info_metadata_fetcher
53
+ )
61
54
 
62
55
  release_date_info
63
56
  rescue StandardError => e
@@ -77,6 +70,39 @@ module Dependabot
77
70
  sig { returns(T.class_of(Dependabot::Version)) }
78
71
  attr_reader :version_class
79
72
 
73
+ sig do
74
+ params(
75
+ repositories: T::Array[T::Hash[String, T.untyped]],
76
+ release_date_info: T::Hash[String, T::Hash[Symbol, T.untyped]],
77
+ dependency_metadata_fetcher: T.proc.params(
78
+ repo: T::Hash[String, T.untyped]
79
+ ).returns(Nokogiri::XML::Document),
80
+ release_info_metadata_fetcher: T.proc.params(
81
+ repo: T::Hash[String, T.untyped]
82
+ ).returns(Nokogiri::HTML::Document)
83
+ ).void
84
+ end
85
+ def parse_repository_release_dates(
86
+ repositories:,
87
+ release_date_info:,
88
+ dependency_metadata_fetcher:,
89
+ release_info_metadata_fetcher:
90
+ )
91
+ repositories.each do |repository_details|
92
+ parse_gradle_plugin_portal_release(
93
+ repository_details,
94
+ release_date_info,
95
+ dependency_metadata_fetcher
96
+ )
97
+
98
+ parse_maven_central_releases(
99
+ repository_details,
100
+ release_date_info,
101
+ release_info_metadata_fetcher
102
+ )
103
+ end
104
+ end
105
+
80
106
  # Parses Maven-style HTML directory listings to extract release dates.
81
107
  sig do
82
108
  params(
@@ -0,0 +1,95 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "dependabot/logger"
5
+ require "sorbet-runtime"
6
+ require "time"
7
+
8
+ module Dependabot
9
+ module Gradle
10
+ module Package
11
+ class VersionReleaseDateFallbackFetcher
12
+ extend T::Sig
13
+
14
+ sig do
15
+ params(
16
+ dependency_name: String,
17
+ repositories: T::Array[T::Hash[String, T.untyped]],
18
+ forbidden_urls: T::Array[String],
19
+ pom_url_builder: T.proc.params(repository_url: String, version: String).returns(String)
20
+ ).void
21
+ end
22
+ def initialize(dependency_name:, repositories:, forbidden_urls:, pom_url_builder:)
23
+ @dependency_name = dependency_name
24
+ @repositories = repositories
25
+ @forbidden_urls = forbidden_urls
26
+ @pom_url_builder = pom_url_builder
27
+ @cache = T.let({}, T::Hash[String, T.nilable(Time)])
28
+ @preferred_repository_url = T.let(nil, T.nilable(String))
29
+ @fallback_logged = T.let(false, T::Boolean)
30
+ end
31
+
32
+ sig { params(version: String).returns(T.nilable(Time)) }
33
+ def fetch(version)
34
+ return @cache[version] if @cache.key?(version)
35
+
36
+ ordered_repositories.each do |repo|
37
+ repository_url = repo.fetch("url")
38
+ pom_url = @pom_url_builder.call(repository_url, version)
39
+
40
+ begin
41
+ response = Dependabot::RegistryClient.head(url: pom_url, headers: repo["auth_headers"])
42
+ last_modified = response.headers["Last-Modified"] || response.headers["last-modified"]
43
+ next unless last_modified
44
+
45
+ released_at = Time.httpdate(last_modified)
46
+ @preferred_repository_url = repository_url
47
+ log_fallback_hit(version: version, repository_url: repository_url, released_at: released_at)
48
+ @cache[version] = released_at
49
+ return released_at
50
+ rescue StandardError => e
51
+ Dependabot.logger.debug(
52
+ "Failed POM Last-Modified fallback for #{@dependency_name} version #{version} from " \
53
+ "#{repository_url}: #{e.message}"
54
+ )
55
+ end
56
+ end
57
+
58
+ Dependabot.logger.debug(
59
+ "No POM Last-Modified fallback release date found for #{@dependency_name} version #{version}"
60
+ )
61
+ @cache[version] = nil
62
+ end
63
+
64
+ private
65
+
66
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
67
+ def ordered_repositories
68
+ candidates = @repositories.reject do |repo|
69
+ @forbidden_urls.include?(repo.fetch("url"))
70
+ end
71
+
72
+ preferred, remaining = candidates.partition do |repo|
73
+ @preferred_repository_url && repo.fetch("url") == @preferred_repository_url
74
+ end
75
+
76
+ preferred + remaining
77
+ end
78
+
79
+ sig { params(version: String, repository_url: String, released_at: Time).void }
80
+ def log_fallback_hit(version:, repository_url:, released_at:)
81
+ Dependabot.logger.debug(
82
+ "Using POM Last-Modified fallback for #{@dependency_name} version #{version} from " \
83
+ "#{repository_url}: #{released_at}"
84
+ )
85
+ return if @fallback_logged
86
+
87
+ Dependabot.logger.info(
88
+ "Using POM Last-Modified fallback release dates for #{@dependency_name} from #{repository_url}"
89
+ )
90
+ @fallback_logged = true
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -232,21 +232,36 @@ module Dependabot
232
232
 
233
233
  sig { params(release: Dependabot::Package::PackageRelease).returns(T::Boolean) }
234
234
  def in_cooldown_period?(release)
235
- unless release.released_at
236
- if cooldown_options
237
- Dependabot.logger.info("Release date not available for version #{release.version} - filtering out")
238
- return true
239
- else
240
- Dependabot.logger.info("Release date not available for version #{release.version}")
241
- return false
242
- end
243
- end
244
-
245
235
  current_version = version_class.correct?(dependency.version) ? version_class.new(dependency.version) : nil
246
236
  days = cooldown_days_for(current_version, release.version)
237
+ return false if days <= 0
238
+
239
+ release = package_details_fetcher.fetch_release_metadata(release: release)
240
+ return missing_release_date_in_cooldown?(release) unless release.released_at
241
+
242
+ cooldown_window?(release: release, days: days)
243
+ end
244
+
245
+ sig { params(release: Dependabot::Package::PackageRelease).returns(T::Boolean) }
246
+ def missing_release_date_in_cooldown?(release)
247
+ if cooldown_options
248
+ Dependabot.logger.info("Release date not available for version #{release.version} - filtering out")
249
+ true
250
+ else
251
+ Dependabot.logger.info("Release date not available for version #{release.version}")
252
+ false
253
+ end
254
+ end
255
+
256
+ sig do
257
+ params(
258
+ release: Dependabot::Package::PackageRelease,
259
+ days: Integer
260
+ ).returns(T::Boolean)
261
+ end
262
+ def cooldown_window?(release:, days:)
247
263
  in_cooldown = Dependabot::UpdateCheckers::CooldownCalculation
248
264
  .within_cooldown_window?(T.must(release.released_at), days)
249
-
250
265
  if in_cooldown
251
266
  passed_days = (Time.now.to_i - release.released_at.to_i) / (24 * 60 * 60)
252
267
  Dependabot.logger.info(
@@ -254,7 +269,6 @@ module Dependabot
254
269
  " Days since release: #{passed_days} (cooldown days: #{days})"
255
270
  )
256
271
  end
257
-
258
272
  in_cooldown
259
273
  end
260
274
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-gradle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.377.0
4
+ version: 0.378.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.377.0
18
+ version: 0.378.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.377.0
25
+ version: 0.378.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: dependabot-maven
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - '='
31
31
  - !ruby/object:Gem::Version
32
- version: 0.377.0
32
+ version: 0.378.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - '='
38
38
  - !ruby/object:Gem::Version
39
- version: 0.377.0
39
+ version: 0.378.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: debug
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -273,6 +273,7 @@ files:
273
273
  - lib/dependabot/gradle/package/distributions_fetcher.rb
274
274
  - lib/dependabot/gradle/package/package_details_fetcher.rb
275
275
  - lib/dependabot/gradle/package/release_date_extractor.rb
276
+ - lib/dependabot/gradle/package/version_release_date_fallback_fetcher.rb
276
277
  - lib/dependabot/gradle/package_manager.rb
277
278
  - lib/dependabot/gradle/requirement.rb
278
279
  - lib/dependabot/gradle/update_checker.rb
@@ -285,7 +286,7 @@ licenses:
285
286
  - MIT
286
287
  metadata:
287
288
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
288
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.377.0
289
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.378.0
289
290
  rdoc_options: []
290
291
  require_paths:
291
292
  - lib