dependabot-common 0.387.0 → 0.388.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 +4 -4
- data/lib/dependabot/clients/github_release.rb +61 -0
- data/lib/dependabot/dependency_requirement.rb +62 -17
- data/lib/dependabot/error_details.rb +51 -0
- data/lib/dependabot/errors.rb +10 -6
- data/lib/dependabot/file_fetchers/base.rb +2 -2
- data/lib/dependabot/git_commit_checker.rb +33 -11
- data/lib/dependabot/git_cooldown_date_resolver.rb +13 -5
- data/lib/dependabot/metadata_finders/base/release_finder.rb +111 -77
- data/lib/dependabot/package/release_cooldown_options.rb +1 -1
- data/lib/dependabot/version.rb +9 -0
- data/lib/dependabot.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29f29c96dbb5e187d5cdecfe015ecd60456ea004a7553a97b812416034451ebf
|
|
4
|
+
data.tar.gz: 1d6e5d04baaf18853476f074e742eb8a60113e2fdfa457db4b3019b1055b30ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c2d09853e0044b7dd6ed6ca9f56501ee8436f113eecec3d85506d1543b79994cb3544cb5718b6f55960f7257af74848e33bc330d956275f98a2d8220f09d4d6
|
|
7
|
+
data.tar.gz: 343ab8ddca784ef0e497987de868f0b405aeeb3bfb8f11c92ad24da3b6d067981fde09397e4d6a5633e2556bfa527f594f432dc4ca9aa79cd9b2b556be930e52
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sawyer"
|
|
5
|
+
require "sorbet-runtime"
|
|
6
|
+
require "time"
|
|
7
|
+
|
|
8
|
+
module Dependabot
|
|
9
|
+
module Clients
|
|
10
|
+
# Typed view over the release fields returned by Octokit.
|
|
11
|
+
class GithubRelease < T::ImmutableStruct
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
const :id, T.nilable(Integer)
|
|
15
|
+
const :name, T.nilable(String)
|
|
16
|
+
const :tag_name, String
|
|
17
|
+
const :body, T.nilable(String)
|
|
18
|
+
const :html_url, T.nilable(String)
|
|
19
|
+
const :prerelease, T::Boolean
|
|
20
|
+
const :published_at, T.nilable(Time)
|
|
21
|
+
|
|
22
|
+
sig { params(resource: Sawyer::Resource).returns(T.nilable(GithubRelease)) }
|
|
23
|
+
def self.from_resource(resource)
|
|
24
|
+
tag_name = T.cast(resource[:tag_name], Object)
|
|
25
|
+
return unless tag_name.is_a?(String)
|
|
26
|
+
|
|
27
|
+
id = T.cast(resource[:id], Object)
|
|
28
|
+
prerelease = T.cast(resource[:prerelease], Object)
|
|
29
|
+
|
|
30
|
+
new(
|
|
31
|
+
id: id.is_a?(Integer) ? id : nil,
|
|
32
|
+
name: string_value(resource, :name),
|
|
33
|
+
tag_name: tag_name,
|
|
34
|
+
body: string_value(resource, :body),
|
|
35
|
+
html_url: string_value(resource, :html_url),
|
|
36
|
+
prerelease: prerelease == true,
|
|
37
|
+
published_at: time_value(resource, :published_at)
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(String)) }
|
|
42
|
+
def self.string_value(resource, key)
|
|
43
|
+
value = T.cast(resource[key], Object)
|
|
44
|
+
value.is_a?(String) ? value : nil
|
|
45
|
+
end
|
|
46
|
+
private_class_method :string_value
|
|
47
|
+
|
|
48
|
+
sig { params(resource: Sawyer::Resource, key: Symbol).returns(T.nilable(Time)) }
|
|
49
|
+
def self.time_value(resource, key)
|
|
50
|
+
value = T.cast(resource[key], Object)
|
|
51
|
+
return value if value.is_a?(Time)
|
|
52
|
+
return unless value.is_a?(String)
|
|
53
|
+
|
|
54
|
+
Time.parse(value)
|
|
55
|
+
rescue ArgumentError
|
|
56
|
+
nil
|
|
57
|
+
end
|
|
58
|
+
private_class_method :time_value
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
@@ -33,18 +33,23 @@ module Dependabot
|
|
|
33
33
|
extend T::Sig
|
|
34
34
|
extend T::Generic
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
Group = T.type_alias { T.any(String, Symbol) }
|
|
37
|
+
ObjectHash = T.type_alias { T::Hash[T.any(Symbol, String), Object] }
|
|
38
|
+
Requirement = T.type_alias { T.any(String, Symbol) }
|
|
39
|
+
Input = T.type_alias { T.any(DependencyRequirement, ObjectHash) }
|
|
40
|
+
|
|
40
41
|
K = type_member { { fixed: Symbol } }
|
|
42
|
+
# Hash-style access remains dynamic until ecosystem callers migrate to the
|
|
43
|
+
# typed readers. Keeping that compatibility here avoids a flag-day change.
|
|
44
|
+
# rubocop:disable Sorbet/ForbidTUntyped
|
|
41
45
|
V = type_member { { fixed: T.untyped } }
|
|
42
46
|
Elem = type_member { { fixed: [Symbol, T.untyped] } }
|
|
47
|
+
# rubocop:enable Sorbet/ForbidTUntyped
|
|
43
48
|
|
|
44
49
|
# Builds a DependencyRequirement from a requirement hash, symbolising
|
|
45
50
|
# top-level keys. Accepts both plain hashes and existing
|
|
46
51
|
# DependencyRequirement instances and always returns a new instance.
|
|
47
|
-
sig { params(hash:
|
|
52
|
+
sig { params(hash: Input).returns(DependencyRequirement) }
|
|
48
53
|
def self.create(hash)
|
|
49
54
|
requirement = new
|
|
50
55
|
requirement.replace(hash.keys.to_h { |k| [k.to_sym, hash[k]] })
|
|
@@ -52,16 +57,21 @@ module Dependabot
|
|
|
52
57
|
end
|
|
53
58
|
|
|
54
59
|
# The version constraint string, e.g. ">= 1.0, < 2.0". Nil when the
|
|
55
|
-
# dependency is pinned by a lockfile rather than a manifest constraint
|
|
56
|
-
|
|
60
|
+
# dependency is pinned by a lockfile rather than a manifest constraint,
|
|
61
|
+
# or :unfixable when no valid updated requirement can be generated.
|
|
62
|
+
sig { returns(T.nilable(Requirement)) }
|
|
57
63
|
def requirement
|
|
58
|
-
self[:requirement]
|
|
64
|
+
value = T.cast(self[:requirement], T.nilable(Object))
|
|
65
|
+
return if value.nil?
|
|
66
|
+
return value if value.is_a?(String) || value == :unfixable
|
|
67
|
+
|
|
68
|
+
raise TypeError, "requirement must be a string, :unfixable, or nil"
|
|
59
69
|
end
|
|
60
70
|
|
|
61
71
|
# The manifest file this requirement was declared in, e.g. "Gemfile".
|
|
62
72
|
sig { returns(T.nilable(String)) }
|
|
63
73
|
def file
|
|
64
|
-
|
|
74
|
+
optional_string(:file)
|
|
65
75
|
end
|
|
66
76
|
|
|
67
77
|
# The dependency groups this requirement belongs to, e.g. ["dev"] or
|
|
@@ -69,26 +79,61 @@ module Dependabot
|
|
|
69
79
|
# Nilable because some requirement entries are constructed with
|
|
70
80
|
# groups: nil, and the reader must reflect that to stay a drop-in for
|
|
71
81
|
# the underlying hash access under sorbet-runtime.
|
|
72
|
-
sig { returns(T.nilable(T::Array[
|
|
82
|
+
sig { returns(T.nilable(T::Array[Group])) }
|
|
73
83
|
def groups
|
|
74
|
-
self[:groups]
|
|
84
|
+
value = T.cast(self[:groups], T.nilable(Object))
|
|
85
|
+
return if value.nil?
|
|
86
|
+
raise TypeError, "groups must be an array of strings or symbols, or nil" unless value.is_a?(Array)
|
|
87
|
+
|
|
88
|
+
value.each do |raw_group|
|
|
89
|
+
group = T.cast(raw_group, Object)
|
|
90
|
+
next if group.is_a?(String) || group.is_a?(Symbol)
|
|
91
|
+
|
|
92
|
+
raise TypeError, "groups must be an array of strings or symbols, or nil"
|
|
93
|
+
end
|
|
94
|
+
value
|
|
75
95
|
end
|
|
76
96
|
|
|
77
97
|
# The source details for the dependency, e.g.
|
|
78
98
|
# { type: "git", url: "https://github.com/..." }. Keys may be symbols
|
|
79
99
|
# or strings depending on whether the requirement was built by a file
|
|
80
100
|
# parser or deserialised from a job definition.
|
|
81
|
-
sig { returns(T.nilable(
|
|
101
|
+
sig { returns(T.nilable(ObjectHash)) }
|
|
82
102
|
def source
|
|
83
|
-
|
|
103
|
+
optional_object_hash(:source)
|
|
84
104
|
end
|
|
85
105
|
|
|
86
106
|
# Optional ecosystem-specific metadata about the requirement, e.g.
|
|
87
107
|
# { property_name: "rails.version" }.
|
|
88
|
-
sig { returns(T.nilable(
|
|
108
|
+
sig { returns(T.nilable(ObjectHash)) }
|
|
89
109
|
def metadata
|
|
90
|
-
|
|
110
|
+
optional_object_hash(:metadata)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
sig { params(key: Symbol).returns(T.nilable(String)) }
|
|
116
|
+
def optional_string(key)
|
|
117
|
+
value = T.cast(self[key], T.nilable(Object))
|
|
118
|
+
return if value.nil?
|
|
119
|
+
return value if value.is_a?(String)
|
|
120
|
+
|
|
121
|
+
raise TypeError, "#{key} must be a string or nil"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
sig { params(key: Symbol).returns(T.nilable(ObjectHash)) }
|
|
125
|
+
def optional_object_hash(key)
|
|
126
|
+
value = T.cast(self[key], T.nilable(Object))
|
|
127
|
+
return if value.nil?
|
|
128
|
+
raise TypeError, "#{key} must be a hash with string or symbol keys, or nil" unless value.is_a?(Hash)
|
|
129
|
+
|
|
130
|
+
value.each_key do |raw_nested_key|
|
|
131
|
+
nested_key = T.cast(raw_nested_key, Object)
|
|
132
|
+
next if nested_key.is_a?(String) || nested_key.is_a?(Symbol)
|
|
133
|
+
|
|
134
|
+
raise TypeError, "#{key} must be a hash with string or symbol keys, or nil"
|
|
135
|
+
end
|
|
136
|
+
value
|
|
91
137
|
end
|
|
92
|
-
# rubocop:enable Sorbet/ForbidTUntyped
|
|
93
138
|
end
|
|
94
139
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
class ErrorDetails < T::ImmutableStruct
|
|
8
|
+
extend T::Sig
|
|
9
|
+
|
|
10
|
+
DetailHash = T.type_alias { T::Hash[T.any(String, Symbol), T.anything] }
|
|
11
|
+
Detail = T.type_alias { T.any(String, DetailHash) }
|
|
12
|
+
|
|
13
|
+
const :error_type, String
|
|
14
|
+
const :error_detail, T.nilable(Detail)
|
|
15
|
+
|
|
16
|
+
sig { params(hash: T::Hash[Symbol, T.anything]).returns(ErrorDetails) }
|
|
17
|
+
def self.from_hash(hash)
|
|
18
|
+
raw_type = T.cast(hash[:"error-type"], Object)
|
|
19
|
+
raise TypeError, "error-type must be a string" unless raw_type.is_a?(String)
|
|
20
|
+
|
|
21
|
+
new(
|
|
22
|
+
error_type: raw_type,
|
|
23
|
+
error_detail: parse_detail(T.cast(hash[:"error-detail"], T.nilable(Object)))
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
|
28
|
+
def to_h
|
|
29
|
+
result = T.let({ "error-type": error_type }, T::Hash[Symbol, Object])
|
|
30
|
+
result[:"error-detail"] = error_detail if error_detail
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sig { params(value: T.nilable(Object)).returns(T.nilable(Detail)) }
|
|
35
|
+
def self.parse_detail(value)
|
|
36
|
+
return if value.nil?
|
|
37
|
+
return value if value.is_a?(String)
|
|
38
|
+
raise TypeError, "error-detail must be a string or hash" unless value.is_a?(Hash)
|
|
39
|
+
|
|
40
|
+
result = T.let({}, DetailHash)
|
|
41
|
+
value.each do |raw_key, raw_value|
|
|
42
|
+
key = T.cast(raw_key, Object)
|
|
43
|
+
raise TypeError, "error-detail keys must be strings or symbols" unless key.is_a?(String) || key.is_a?(Symbol)
|
|
44
|
+
|
|
45
|
+
result[key] = T.cast(raw_value, Object)
|
|
46
|
+
end
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
private_class_method :parse_detail
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/dependabot/errors.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/error_details"
|
|
5
6
|
require "dependabot/utils"
|
|
6
7
|
|
|
7
8
|
# rubocop:disable Metrics/ModuleLength
|
|
@@ -23,7 +24,7 @@ module Dependabot
|
|
|
23
24
|
|
|
24
25
|
# rubocop:disable Metrics/MethodLength
|
|
25
26
|
# rubocop:disable Metrics/CyclomaticComplexity
|
|
26
|
-
sig { params(error: StandardError).returns(T.nilable(
|
|
27
|
+
sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
|
|
27
28
|
def self.fetcher_error_details(error)
|
|
28
29
|
case error
|
|
29
30
|
when Dependabot::ToolVersionNotSupported
|
|
@@ -138,11 +139,12 @@ module Dependabot
|
|
|
138
139
|
"rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
|
|
139
140
|
}
|
|
140
141
|
}
|
|
141
|
-
end
|
|
142
|
+
end => details
|
|
143
|
+
Dependabot::ErrorDetails.from_hash(details) if details
|
|
142
144
|
end
|
|
143
145
|
# rubocop:enable Metrics/CyclomaticComplexity
|
|
144
146
|
|
|
145
|
-
sig { params(error: StandardError).returns(T.nilable(
|
|
147
|
+
sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
|
|
146
148
|
def self.parser_error_details(error)
|
|
147
149
|
case error
|
|
148
150
|
when Dependabot::ToolFeatureNotSupported
|
|
@@ -220,13 +222,14 @@ module Dependabot
|
|
|
220
222
|
# and responsibility for fixing it is on them, not us. As a result we
|
|
221
223
|
# quietly log these as errors
|
|
222
224
|
{ "error-type": "server_error" }
|
|
223
|
-
end
|
|
225
|
+
end => details
|
|
226
|
+
Dependabot::ErrorDetails.from_hash(details) if details
|
|
224
227
|
end
|
|
225
228
|
|
|
226
229
|
# rubocop:disable Lint/RedundantCopDisableDirective
|
|
227
230
|
# rubocop:disable Metrics/CyclomaticComplexity
|
|
228
231
|
# rubocop:disable Metrics/AbcSize
|
|
229
|
-
sig { params(error: StandardError).returns(T.nilable(
|
|
232
|
+
sig { params(error: StandardError).returns(T.nilable(Dependabot::ErrorDetails)) }
|
|
230
233
|
def self.updater_error_details(error)
|
|
231
234
|
case error
|
|
232
235
|
when Dependabot::ToolFeatureNotSupported
|
|
@@ -400,7 +403,8 @@ module Dependabot
|
|
|
400
403
|
"rate-limit-reset": T.cast(error, Octokit::Error).response_headers["X-RateLimit-Reset"]
|
|
401
404
|
}
|
|
402
405
|
}
|
|
403
|
-
end
|
|
406
|
+
end => details
|
|
407
|
+
Dependabot::ErrorDetails.from_hash(details) if details
|
|
404
408
|
end
|
|
405
409
|
|
|
406
410
|
# rubocop:enable Metrics/MethodLength
|
|
@@ -70,7 +70,7 @@ module Dependabot
|
|
|
70
70
|
sig { returns(T.nilable(String)) }
|
|
71
71
|
attr_reader :repo_contents_path
|
|
72
72
|
|
|
73
|
-
sig { returns(T::Hash[
|
|
73
|
+
sig { returns(T::Hash[Symbol, Object]) }
|
|
74
74
|
attr_reader :options
|
|
75
75
|
|
|
76
76
|
CLIENT_NOT_FOUND_ERRORS = T.let(
|
|
@@ -135,7 +135,7 @@ module Dependabot
|
|
|
135
135
|
source: Dependabot::Source,
|
|
136
136
|
credentials: T::Array[Dependabot::Credential],
|
|
137
137
|
repo_contents_path: T.nilable(String),
|
|
138
|
-
options: T::Hash[
|
|
138
|
+
options: T::Hash[Symbol, Object],
|
|
139
139
|
update_config: T.nilable(Dependabot::Config::UpdateConfig)
|
|
140
140
|
)
|
|
141
141
|
.void
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "excon"
|
|
@@ -14,6 +14,7 @@ require "dependabot/utils"
|
|
|
14
14
|
require "dependabot/source"
|
|
15
15
|
require "dependabot/dependency"
|
|
16
16
|
require "dependabot/credential"
|
|
17
|
+
require "dependabot/clients/github_release"
|
|
17
18
|
require "dependabot/git_metadata_fetcher"
|
|
18
19
|
require "dependabot/git_commit_checker/source_details"
|
|
19
20
|
require "dependabot/git_tag_details"
|
|
@@ -485,7 +486,9 @@ module Dependabot
|
|
|
485
486
|
def handle_tag_prefix(tags)
|
|
486
487
|
if dependency_source_details&.ref&.start_with?("tags/")
|
|
487
488
|
tags = tags.map do |tag|
|
|
488
|
-
|
|
489
|
+
duplicated_tag = tag.dup
|
|
490
|
+
duplicated_tag.name = "tags/#{tag.name}"
|
|
491
|
+
duplicated_tag
|
|
489
492
|
end
|
|
490
493
|
end
|
|
491
494
|
|
|
@@ -524,8 +527,11 @@ module Dependabot
|
|
|
524
527
|
client = Clients::GithubWithRetries
|
|
525
528
|
.for_github_dot_com(credentials: credentials)
|
|
526
529
|
|
|
527
|
-
|
|
528
|
-
T.
|
|
530
|
+
comparison = client.compare(T.must(listing_source_repo), ref1, ref2)
|
|
531
|
+
status = T.cast(comparison[:status], Object)
|
|
532
|
+
raise TypeError, "GitHub comparison status must be a string" unless status.is_a?(String)
|
|
533
|
+
|
|
534
|
+
status
|
|
529
535
|
end
|
|
530
536
|
|
|
531
537
|
sig { params(ref1: String, ref2: String).returns(String) }
|
|
@@ -534,9 +540,11 @@ module Dependabot
|
|
|
534
540
|
.for_gitlab_dot_com(credentials: credentials)
|
|
535
541
|
|
|
536
542
|
comparison = client.compare(T.must(listing_source_repo), ref1, ref2)
|
|
543
|
+
commits = T.cast(comparison["commits"], Object)
|
|
544
|
+
raise TypeError, "GitLab comparison commits must be an array" unless commits.is_a?(Array)
|
|
537
545
|
|
|
538
|
-
if
|
|
539
|
-
elsif T.
|
|
546
|
+
if commits.empty? then "behind"
|
|
547
|
+
elsif T.cast(comparison["compare_same_ref"], Object) == true then "identical"
|
|
540
548
|
else
|
|
541
549
|
"ahead"
|
|
542
550
|
end
|
|
@@ -555,7 +563,13 @@ module Dependabot
|
|
|
555
563
|
|
|
556
564
|
# Conservatively assume that ref2 is ahead in the equality case, of
|
|
557
565
|
# if we get an unexpected format (e.g., due to a 404)
|
|
558
|
-
|
|
566
|
+
comparison = T.cast(JSON.parse(response.body), Object)
|
|
567
|
+
raise TypeError, "Bitbucket comparison must be a hash" unless comparison.is_a?(Hash)
|
|
568
|
+
|
|
569
|
+
values = T.cast(comparison.fetch("values", ["x"]), Object)
|
|
570
|
+
raise TypeError, "Bitbucket comparison values must be an array" unless values.is_a?(Array)
|
|
571
|
+
|
|
572
|
+
if values.empty? then "behind"
|
|
559
573
|
else
|
|
560
574
|
"ahead"
|
|
561
575
|
end
|
|
@@ -674,7 +688,9 @@ module Dependabot
|
|
|
674
688
|
|
|
675
689
|
if dependency_source_details&.ref&.start_with?("tags/")
|
|
676
690
|
tags = tags.map do |tag|
|
|
677
|
-
|
|
691
|
+
duplicated_tag = tag.dup
|
|
692
|
+
duplicated_tag.name = "tags/#{tag.name}"
|
|
693
|
+
duplicated_tag
|
|
678
694
|
end
|
|
679
695
|
end
|
|
680
696
|
|
|
@@ -737,7 +753,7 @@ module Dependabot
|
|
|
737
753
|
false
|
|
738
754
|
end
|
|
739
755
|
|
|
740
|
-
sig { returns(T::Array[
|
|
756
|
+
sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
|
|
741
757
|
def github_releases
|
|
742
758
|
@github_releases ||= T.let(
|
|
743
759
|
begin
|
|
@@ -752,11 +768,17 @@ module Dependabot
|
|
|
752
768
|
)
|
|
753
769
|
# The Octokit RBI types this as non-nil, but it can be nil at runtime
|
|
754
770
|
# (e.g. an empty/unexpected registry response), so guard against it.
|
|
755
|
-
|
|
771
|
+
releases = T.let(
|
|
772
|
+
client.releases(T.must(source).repo, per_page: 100),
|
|
773
|
+
T.nilable(T::Array[Sawyer::Resource])
|
|
774
|
+
)
|
|
775
|
+
(releases || []).filter_map do |release|
|
|
776
|
+
Dependabot::Clients::GithubRelease.from_resource(release)
|
|
777
|
+
end
|
|
756
778
|
rescue Octokit::Error
|
|
757
779
|
[]
|
|
758
780
|
end,
|
|
759
|
-
T.nilable(T::Array[
|
|
781
|
+
T.nilable(T::Array[Dependabot::Clients::GithubRelease])
|
|
760
782
|
)
|
|
761
783
|
end
|
|
762
784
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/clients/github_release"
|
|
5
6
|
require "dependabot/clients/github_with_retries"
|
|
6
7
|
require "dependabot/shared_helpers"
|
|
7
8
|
require "dependabot/source"
|
|
@@ -45,7 +46,8 @@ module Dependabot
|
|
|
45
46
|
releases = cached_github_releases
|
|
46
47
|
unless releases.empty?
|
|
47
48
|
release = releases.find { |r| r.tag_name == tag_name }
|
|
48
|
-
|
|
49
|
+
published_at = release&.published_at
|
|
50
|
+
return published_at if published_at
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
tag_creation_date(tag_name, commit_sha)
|
|
@@ -88,7 +90,7 @@ module Dependabot
|
|
|
88
90
|
|
|
89
91
|
# Fetches and caches GitHub releases for the dependency source.
|
|
90
92
|
# Returns an empty array for non-GitHub sources.
|
|
91
|
-
sig { returns(T::Array[
|
|
93
|
+
sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
|
|
92
94
|
def cached_github_releases
|
|
93
95
|
@cached_github_releases ||= T.let(
|
|
94
96
|
begin
|
|
@@ -99,7 +101,13 @@ module Dependabot
|
|
|
99
101
|
source: T.must(source),
|
|
100
102
|
credentials: cooldown_credentials
|
|
101
103
|
)
|
|
102
|
-
|
|
104
|
+
releases = T.let(
|
|
105
|
+
client.releases(T.must(source).repo, per_page: 100),
|
|
106
|
+
T.nilable(T::Array[Sawyer::Resource])
|
|
107
|
+
)
|
|
108
|
+
(releases || []).filter_map do |release|
|
|
109
|
+
Dependabot::Clients::GithubRelease.from_resource(release)
|
|
110
|
+
end
|
|
103
111
|
else
|
|
104
112
|
[]
|
|
105
113
|
end
|
|
@@ -107,7 +115,7 @@ module Dependabot
|
|
|
107
115
|
Dependabot.logger.debug("Error fetching GitHub releases: #{e.message}")
|
|
108
116
|
[]
|
|
109
117
|
end,
|
|
110
|
-
T.nilable(T::Array[
|
|
118
|
+
T.nilable(T::Array[Dependabot::Clients::GithubRelease])
|
|
111
119
|
)
|
|
112
120
|
end
|
|
113
121
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: strong
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require "sorbet-runtime"
|
|
5
|
+
require "time"
|
|
5
6
|
|
|
6
7
|
require "dependabot/credential"
|
|
8
|
+
require "dependabot/clients/github_release"
|
|
7
9
|
require "dependabot/clients/github_with_retries"
|
|
8
10
|
require "dependabot/clients/gitlab_with_retries"
|
|
9
11
|
require "dependabot/metadata_finders/base"
|
|
@@ -12,40 +14,62 @@ require "dependabot/utils"
|
|
|
12
14
|
module Dependabot
|
|
13
15
|
module MetadataFinders
|
|
14
16
|
class Base
|
|
15
|
-
class GitLabRelease
|
|
17
|
+
class GitLabRelease < T::ImmutableStruct
|
|
16
18
|
extend T::Sig
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
sig { returns(String) }
|
|
25
|
-
attr_reader :body
|
|
26
|
-
|
|
27
|
-
sig { returns(String) }
|
|
28
|
-
attr_reader :html_url
|
|
20
|
+
const :name, String
|
|
21
|
+
const :tag_name, String
|
|
22
|
+
const :body, T.nilable(String)
|
|
23
|
+
const :html_url, String
|
|
24
|
+
const :authored_at, Time
|
|
29
25
|
|
|
30
26
|
sig do
|
|
31
27
|
params(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
html_url: String
|
|
36
|
-
).void
|
|
37
|
-
end
|
|
38
|
-
def initialize(name:, tag_name:, body:, html_url:)
|
|
39
|
-
@name = name
|
|
40
|
-
@tag_name = tag_name
|
|
41
|
-
@body = body
|
|
42
|
-
@html_url = html_url
|
|
28
|
+
tag: Gitlab::ObjectifiedHash,
|
|
29
|
+
source_url: String
|
|
30
|
+
).returns(T.nilable(GitLabRelease))
|
|
43
31
|
end
|
|
32
|
+
def self.from_tag(tag, source_url:)
|
|
33
|
+
name = T.cast(tag["name"], Object)
|
|
34
|
+
release = T.cast(tag["release"], Object)
|
|
35
|
+
commit = T.cast(tag["commit"], Object)
|
|
36
|
+
return unless name.is_a?(String)
|
|
37
|
+
return unless release.is_a?(Gitlab::ObjectifiedHash)
|
|
38
|
+
return unless commit.is_a?(Gitlab::ObjectifiedHash)
|
|
39
|
+
|
|
40
|
+
tag_name = T.cast(release["tag_name"], Object)
|
|
41
|
+
return unless tag_name.is_a?(String)
|
|
42
|
+
|
|
43
|
+
authored_at = time_value(T.cast(commit["authored_date"], Object))
|
|
44
|
+
return unless authored_at
|
|
45
|
+
|
|
46
|
+
description = T.cast(release["description"], Object)
|
|
47
|
+
new(
|
|
48
|
+
name: name,
|
|
49
|
+
tag_name: tag_name,
|
|
50
|
+
body: description.is_a?(String) ? description : nil,
|
|
51
|
+
html_url: "#{source_url}/tags/#{name}",
|
|
52
|
+
authored_at: authored_at
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
sig { params(value: Object).returns(T.nilable(Time)) }
|
|
57
|
+
def self.time_value(value)
|
|
58
|
+
return value if value.is_a?(Time)
|
|
59
|
+
return unless value.is_a?(String)
|
|
60
|
+
|
|
61
|
+
Time.parse(value)
|
|
62
|
+
rescue ArgumentError
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
private_class_method :time_value
|
|
44
66
|
end
|
|
45
67
|
|
|
46
68
|
class ReleaseFinder
|
|
47
69
|
extend T::Sig
|
|
48
70
|
|
|
71
|
+
ReleaseType = T.type_alias { T.any(Dependabot::Clients::GithubRelease, GitLabRelease) }
|
|
72
|
+
|
|
49
73
|
sig { returns(Dependabot::Dependency) }
|
|
50
74
|
attr_reader :dependency
|
|
51
75
|
|
|
@@ -98,14 +122,13 @@ module Dependabot
|
|
|
98
122
|
|
|
99
123
|
private
|
|
100
124
|
|
|
101
|
-
sig { returns(T::Array[
|
|
125
|
+
sig { returns(T::Array[ReleaseType]) }
|
|
102
126
|
def all_dep_releases
|
|
103
127
|
releases = all_releases
|
|
104
128
|
dep_prefix = dependency.name.downcase
|
|
105
129
|
|
|
106
130
|
releases_with_dependency_name =
|
|
107
131
|
releases
|
|
108
|
-
.reject { |r| r.tag_name.nil? }
|
|
109
132
|
.select { |r| r.tag_name.start_with?(dep_prefix) }
|
|
110
133
|
|
|
111
134
|
return releases unless releases_with_dependency_name.any?
|
|
@@ -113,12 +136,12 @@ module Dependabot
|
|
|
113
136
|
releases_with_dependency_name
|
|
114
137
|
end
|
|
115
138
|
|
|
116
|
-
sig { returns(T::Array[
|
|
139
|
+
sig { returns(T::Array[ReleaseType]) }
|
|
117
140
|
def all_releases
|
|
118
|
-
@all_releases ||= T.let(fetch_dependency_releases, T.nilable(T::Array[
|
|
141
|
+
@all_releases ||= T.let(fetch_dependency_releases, T.nilable(T::Array[ReleaseType]))
|
|
119
142
|
end
|
|
120
143
|
|
|
121
|
-
sig { returns(T.nilable(T::Array[
|
|
144
|
+
sig { returns(T.nilable(T::Array[ReleaseType])) }
|
|
122
145
|
def relevant_releases
|
|
123
146
|
releases = releases_since_previous_version
|
|
124
147
|
|
|
@@ -139,7 +162,7 @@ module Dependabot
|
|
|
139
162
|
end
|
|
140
163
|
end
|
|
141
164
|
|
|
142
|
-
sig { returns(T.nilable(T::Array[
|
|
165
|
+
sig { returns(T.nilable(T::Array[ReleaseType])) }
|
|
143
166
|
def releases_since_previous_version
|
|
144
167
|
return [updated_release].compact unless previous_version
|
|
145
168
|
|
|
@@ -158,21 +181,21 @@ module Dependabot
|
|
|
158
181
|
end
|
|
159
182
|
end
|
|
160
183
|
|
|
161
|
-
sig { params(releases: T::Array[
|
|
184
|
+
sig { params(releases: T::Array[ReleaseType]).returns(T.nilable(T::Array[ReleaseType])) }
|
|
162
185
|
def filter_releases_using_previous_release(releases)
|
|
163
186
|
return releases if releases.index(previous_release).nil?
|
|
164
187
|
|
|
165
188
|
releases.first(T.must(releases.index(previous_release)))
|
|
166
189
|
end
|
|
167
190
|
|
|
168
|
-
sig { params(releases: T::Array[
|
|
191
|
+
sig { params(releases: T::Array[ReleaseType]).returns(T.nilable(T::Array[ReleaseType])) }
|
|
169
192
|
def filter_releases_using_updated_release(releases)
|
|
170
193
|
return releases if releases.index(updated_release).nil?
|
|
171
194
|
|
|
172
195
|
releases[releases.index(updated_release)..-1]
|
|
173
196
|
end
|
|
174
197
|
|
|
175
|
-
sig { params(releases: T::Array[
|
|
198
|
+
sig { params(releases: T::Array[ReleaseType], conservative: T::Boolean).returns(T::Array[ReleaseType]) }
|
|
176
199
|
def filter_releases_using_previous_version(releases, conservative:)
|
|
177
200
|
releases.reject do |release|
|
|
178
201
|
cleaned_tag = release.tag_name.gsub(/^[^0-9]*/, "")
|
|
@@ -193,7 +216,7 @@ module Dependabot
|
|
|
193
216
|
end
|
|
194
217
|
end
|
|
195
218
|
|
|
196
|
-
sig { params(releases: T::Array[
|
|
219
|
+
sig { params(releases: T::Array[ReleaseType], conservative: T::Boolean).returns(T::Array[ReleaseType]) }
|
|
197
220
|
def filter_releases_using_updated_version(releases, conservative:)
|
|
198
221
|
updated_version = version_class.new(new_version)
|
|
199
222
|
|
|
@@ -216,17 +239,17 @@ module Dependabot
|
|
|
216
239
|
end
|
|
217
240
|
end
|
|
218
241
|
|
|
219
|
-
sig { returns(T.
|
|
242
|
+
sig { returns(T.nilable(ReleaseType)) }
|
|
220
243
|
def updated_release
|
|
221
244
|
release_for_version(new_version)
|
|
222
245
|
end
|
|
223
246
|
|
|
224
|
-
sig { returns(T.
|
|
247
|
+
sig { returns(T.nilable(ReleaseType)) }
|
|
225
248
|
def previous_release
|
|
226
249
|
release_for_version(previous_version)
|
|
227
250
|
end
|
|
228
251
|
|
|
229
|
-
sig { params(version: T.nilable(String)).returns(T.
|
|
252
|
+
sig { params(version: T.nilable(String)).returns(T.nilable(ReleaseType)) }
|
|
230
253
|
def release_for_version(version)
|
|
231
254
|
return nil unless version
|
|
232
255
|
|
|
@@ -236,22 +259,23 @@ module Dependabot
|
|
|
236
259
|
all_dep_releases.find { |r| release_regex.match?(r.name.to_s) }
|
|
237
260
|
end
|
|
238
261
|
|
|
239
|
-
sig { params(release:
|
|
262
|
+
sig { params(release: ReleaseType).returns(String) }
|
|
240
263
|
def serialize_release(release)
|
|
241
|
-
|
|
242
|
-
title = "## #{
|
|
243
|
-
body = if
|
|
264
|
+
name = release.name
|
|
265
|
+
title = "## #{name.to_s == '' ? release.tag_name : name}\n"
|
|
266
|
+
body = if release.body.to_s.gsub(/\n*\z/m, "") == ""
|
|
244
267
|
"No release notes provided."
|
|
245
268
|
else
|
|
246
|
-
|
|
269
|
+
T.must(release.body).gsub(/\n*\z/m, "")
|
|
247
270
|
end
|
|
248
271
|
|
|
249
|
-
release_body_includes_title?(
|
|
272
|
+
release_body_includes_title?(release) ? body : title + body
|
|
250
273
|
end
|
|
251
274
|
|
|
252
|
-
sig { params(release:
|
|
275
|
+
sig { params(release: ReleaseType).returns(T::Boolean) }
|
|
253
276
|
def release_body_includes_title?(release)
|
|
254
|
-
|
|
277
|
+
name = release.name
|
|
278
|
+
title = name.nil? || name.empty? ? release.tag_name : name
|
|
255
279
|
release.body.to_s.match?(/\A\s*\#*\s*#{Regexp.quote(title)}/m)
|
|
256
280
|
end
|
|
257
281
|
|
|
@@ -265,7 +289,7 @@ module Dependabot
|
|
|
265
289
|
dependency.version_class
|
|
266
290
|
end
|
|
267
291
|
|
|
268
|
-
sig { returns(T::Array[
|
|
292
|
+
sig { returns(T::Array[ReleaseType]) }
|
|
269
293
|
def fetch_dependency_releases
|
|
270
294
|
return [] unless source
|
|
271
295
|
|
|
@@ -279,47 +303,45 @@ module Dependabot
|
|
|
279
303
|
end
|
|
280
304
|
end
|
|
281
305
|
|
|
282
|
-
sig { returns(T::Array[
|
|
306
|
+
sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
|
|
283
307
|
def fetch_github_releases
|
|
284
|
-
releases =
|
|
285
|
-
|
|
286
|
-
# Remove any releases without a tag name. These are draft releases and
|
|
287
|
-
# aren't yet associated with a tag, so shouldn't be used.
|
|
288
|
-
releases = releases.reject { |r| T.unsafe(r).tag_name.nil? }
|
|
308
|
+
releases = parsed_github_releases
|
|
289
309
|
|
|
290
310
|
clean_release_names =
|
|
291
|
-
releases.map { |
|
|
311
|
+
releases.map { |release| release.tag_name.gsub(/^[^0-9\.]*/, "") }
|
|
292
312
|
|
|
293
313
|
if clean_release_names.all? { |nm| version_class.correct?(nm) }
|
|
294
|
-
releases.sort_by do |
|
|
295
|
-
version_class.new(
|
|
314
|
+
releases.sort_by do |release|
|
|
315
|
+
version_class.new(release.tag_name.gsub(/^[^0-9\.]*/, ""))
|
|
296
316
|
end.reverse
|
|
297
317
|
else
|
|
298
|
-
releases.sort_by { |
|
|
318
|
+
releases.sort_by { |release| release.id || 0 }.reverse
|
|
299
319
|
end
|
|
300
320
|
rescue Octokit::NotFound, Octokit::UnavailableForLegalReasons
|
|
301
321
|
[]
|
|
302
322
|
end
|
|
303
323
|
|
|
304
|
-
sig { returns(T::Array[
|
|
324
|
+
sig { returns(T::Array[Dependabot::Clients::GithubRelease]) }
|
|
325
|
+
def parsed_github_releases
|
|
326
|
+
resources = T.let(
|
|
327
|
+
github_client.releases(T.must(source).repo, per_page: 100),
|
|
328
|
+
T.nilable(T::Array[Sawyer::Resource])
|
|
329
|
+
)
|
|
330
|
+
releases = (resources || []).filter_map do |release|
|
|
331
|
+
Dependabot::Clients::GithubRelease.from_resource(release)
|
|
332
|
+
end
|
|
333
|
+
releases
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
sig { returns(T::Array[GitLabRelease]) }
|
|
305
337
|
def fetch_gitlab_releases
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
)
|
|
311
|
-
.select(&:release)
|
|
312
|
-
.sort_by { |r| T.unsafe(r).commit.authored_date }
|
|
313
|
-
.reverse
|
|
314
|
-
|
|
315
|
-
releases.map do |tag|
|
|
316
|
-
GitLabRelease.new(
|
|
317
|
-
name: T.unsafe(tag).name,
|
|
318
|
-
tag_name: T.unsafe(tag).release.tag_name,
|
|
319
|
-
body: T.unsafe(tag).release.description,
|
|
320
|
-
html_url: "#{T.must(source).url}/tags/#{T.unsafe(tag).name}"
|
|
321
|
-
)
|
|
338
|
+
tags = gitlab_client.tags(T.must(source).repo)
|
|
339
|
+
releases = tags.filter_map do |tag|
|
|
340
|
+
next unless tag.is_a?(Gitlab::ObjectifiedHash)
|
|
341
|
+
|
|
342
|
+
GitLabRelease.from_tag(tag, source_url: T.must(source).url)
|
|
322
343
|
end
|
|
344
|
+
releases.sort_by(&:authored_at).reverse
|
|
323
345
|
rescue Gitlab::Error::NotFound
|
|
324
346
|
[]
|
|
325
347
|
end
|
|
@@ -359,20 +381,32 @@ module Dependabot
|
|
|
359
381
|
|
|
360
382
|
sig { returns(T.nilable(String)) }
|
|
361
383
|
def previous_ref
|
|
362
|
-
previous_refs = T.must(dependency.previous_requirements).filter_map do |
|
|
363
|
-
|
|
384
|
+
previous_refs = T.must(dependency.previous_requirements).filter_map do |requirement|
|
|
385
|
+
requirement_ref(requirement)
|
|
364
386
|
end.uniq
|
|
365
387
|
previous_refs.first if previous_refs.one?
|
|
366
388
|
end
|
|
367
389
|
|
|
368
390
|
sig { returns(T.nilable(String)) }
|
|
369
391
|
def new_ref
|
|
370
|
-
new_refs = dependency.requirements.filter_map do |
|
|
371
|
-
|
|
392
|
+
new_refs = dependency.requirements.filter_map do |requirement|
|
|
393
|
+
requirement_ref(requirement)
|
|
372
394
|
end.uniq
|
|
373
395
|
new_refs.first if new_refs.one?
|
|
374
396
|
end
|
|
375
397
|
|
|
398
|
+
sig { params(requirement: Dependabot::DependencyRequirement).returns(T.nilable(String)) }
|
|
399
|
+
def requirement_ref(requirement)
|
|
400
|
+
source = requirement.source
|
|
401
|
+
return unless source
|
|
402
|
+
|
|
403
|
+
symbol_ref = source[:ref]
|
|
404
|
+
return symbol_ref if symbol_ref.is_a?(String)
|
|
405
|
+
|
|
406
|
+
string_ref = source["ref"]
|
|
407
|
+
string_ref if string_ref.is_a?(String)
|
|
408
|
+
end
|
|
409
|
+
|
|
376
410
|
sig { returns(T::Boolean) }
|
|
377
411
|
def ref_changed?
|
|
378
412
|
# We could go from multiple previous refs (nil) to a single new ref
|
data/lib/dependabot/version.rb
CHANGED
|
@@ -24,6 +24,15 @@ module Dependabot
|
|
|
24
24
|
T.cast(super, Dependabot::Version)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
sig do
|
|
28
|
+
overridable
|
|
29
|
+
.params(_from_version: String, _to_version: String)
|
|
30
|
+
.returns(T.nilable(String))
|
|
31
|
+
end
|
|
32
|
+
def self.update_type(_from_version, _to_version)
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
27
36
|
# Opt-in to Rubygems 4 behavior
|
|
28
37
|
sig { override.overridable.params(version: VersionParameter).returns(T::Boolean) }
|
|
29
38
|
def self.correct?(version)
|
data/lib/dependabot.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-common
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.388.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
@@ -518,6 +518,7 @@ files:
|
|
|
518
518
|
- lib/dependabot/clients/bitbucket.rb
|
|
519
519
|
- lib/dependabot/clients/bitbucket_with_retries.rb
|
|
520
520
|
- lib/dependabot/clients/codecommit.rb
|
|
521
|
+
- lib/dependabot/clients/github_release.rb
|
|
521
522
|
- lib/dependabot/clients/github_with_retries.rb
|
|
522
523
|
- lib/dependabot/clients/gitlab_with_retries.rb
|
|
523
524
|
- lib/dependabot/command_helpers.rb
|
|
@@ -536,6 +537,7 @@ files:
|
|
|
536
537
|
- lib/dependabot/dependency_group.rb
|
|
537
538
|
- lib/dependabot/dependency_requirement.rb
|
|
538
539
|
- lib/dependabot/ecosystem.rb
|
|
540
|
+
- lib/dependabot/error_details.rb
|
|
539
541
|
- lib/dependabot/errors.rb
|
|
540
542
|
- lib/dependabot/experiments.rb
|
|
541
543
|
- lib/dependabot/file_fetchers.rb
|
|
@@ -623,7 +625,7 @@ licenses:
|
|
|
623
625
|
- MIT
|
|
624
626
|
metadata:
|
|
625
627
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
626
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
628
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.388.0
|
|
627
629
|
rdoc_options: []
|
|
628
630
|
require_paths:
|
|
629
631
|
- lib
|