dependabot-docker 0.259.0 → 0.260.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dependabot/docker/file_fetcher.rb +28 -13
- data/lib/dependabot/docker/file_parser.rb +20 -3
- data/lib/dependabot/docker/file_updater.rb +54 -26
- data/lib/dependabot/docker/metadata_finder.rb +5 -1
- data/lib/dependabot/docker/requirement.rb +4 -2
- data/lib/dependabot/docker/tag.rb +33 -15
- data/lib/dependabot/docker/update_checker.rb +1 -1
- data/lib/dependabot/docker/utils/credentials_finder.rb +15 -7
- data/lib/dependabot/docker/utils/helpers.rb +6 -1
- data/lib/dependabot/docker/version.rb +21 -9
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2b8bc30ebbc60146a41cb00193a87642254cea63e48d63a6ceb5f0bd3305e63
|
4
|
+
data.tar.gz: 8085d1d31692622d6ee57bbdc0466e388257275fb7aaa3841a6a426192f9f5ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95e886db37f7981efa16b2c00569b3eb37cb5dc5ebcb4824d34cb2d776d25276c982597088451d8cbdf2816fddf19418c0979c699e80529d5c63b5fef34dcbd2
|
7
|
+
data.tar.gz: c9034900857a4f7714769f3639da527f8cfdc2d70ab2591b54718e33844c70c76364c6cc8e808523988bbd7f78bb6a6c7c97fcacec14ed15b8b4ecd7290ace09
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "sorbet-runtime"
|
@@ -15,11 +15,13 @@ module Dependabot
|
|
15
15
|
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
16
16
|
DOCKER_REGEXP = /dockerfile/i
|
17
17
|
|
18
|
+
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
18
19
|
def self.required_files_in?(filenames)
|
19
20
|
filenames.any? { |f| f.match?(DOCKER_REGEXP) } or
|
20
21
|
filenames.any? { |f| f.match?(YAML_REGEXP) }
|
21
22
|
end
|
22
23
|
|
24
|
+
sig { override.returns(String) }
|
23
25
|
def self.required_files_message
|
24
26
|
"Repo must contain a Dockerfile or Kubernetes YAML files."
|
25
27
|
end
|
@@ -40,54 +42,66 @@ module Dependabot
|
|
40
42
|
elsif incorrectly_encoded_dockerfiles.none?
|
41
43
|
raise(
|
42
44
|
Dependabot::DependencyFileNotParseable,
|
43
|
-
incorrectly_encoded_yamlfiles.first.path
|
45
|
+
T.must(incorrectly_encoded_yamlfiles.first).path
|
44
46
|
)
|
45
47
|
else
|
46
48
|
raise(
|
47
49
|
Dependabot::DependencyFileNotParseable,
|
48
|
-
incorrectly_encoded_dockerfiles.first.path
|
50
|
+
T.must(incorrectly_encoded_dockerfiles.first).path
|
49
51
|
)
|
50
52
|
end
|
51
53
|
end
|
52
54
|
|
53
55
|
private
|
54
56
|
|
57
|
+
sig { returns(T::Array[DependencyFile]) }
|
55
58
|
def dockerfiles
|
56
|
-
@dockerfiles ||=
|
57
|
-
|
59
|
+
@dockerfiles ||= T.let(fetch_dockerfiles, T.nilable(T::Array[DependencyFile]))
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
63
|
+
def fetch_dockerfiles
|
64
|
+
repo_contents(raise_errors: false)
|
58
65
|
.select { |f| f.type == "file" && f.name.match?(DOCKER_REGEXP) }
|
59
66
|
.map { |f| fetch_file_from_host(f.name) }
|
60
67
|
end
|
61
68
|
|
69
|
+
sig { returns(T::Array[DependencyFile]) }
|
62
70
|
def correctly_encoded_dockerfiles
|
63
|
-
dockerfiles.select { |f| f.content
|
71
|
+
dockerfiles.select { |f| f.content&.valid_encoding? }
|
64
72
|
end
|
65
73
|
|
74
|
+
sig { returns(T::Array[DependencyFile]) }
|
66
75
|
def incorrectly_encoded_dockerfiles
|
67
|
-
dockerfiles.reject { |f| f.content
|
76
|
+
dockerfiles.reject { |f| f.content&.valid_encoding? }
|
68
77
|
end
|
69
78
|
|
79
|
+
sig { returns(T::Array[DependencyFile]) }
|
70
80
|
def yamlfiles
|
71
|
-
@yamlfiles ||=
|
81
|
+
@yamlfiles ||= T.let(
|
72
82
|
repo_contents(raise_errors: false)
|
73
|
-
|
74
|
-
|
83
|
+
.select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
84
|
+
.map { |f| fetch_file_from_host(f.name) },
|
85
|
+
T.nilable(T::Array[DependencyFile])
|
86
|
+
)
|
75
87
|
end
|
76
88
|
|
89
|
+
sig { params(resource: Object).returns(T.nilable(T::Boolean)) }
|
77
90
|
def likely_kubernetes_resource?(resource)
|
78
91
|
# Heuristic for being a Kubernetes resource. We could make this tighter but this probably works well.
|
79
92
|
resource.is_a?(::Hash) && resource.key?("apiVersion") && resource.key?("kind")
|
80
93
|
end
|
81
94
|
|
95
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
82
96
|
def correctly_encoded_yamlfiles
|
83
|
-
candidate_files = yamlfiles.select { |f| f.content
|
97
|
+
candidate_files = yamlfiles.select { |f| f.content&.valid_encoding? }
|
84
98
|
candidate_files.select do |f|
|
85
99
|
if f.type == "file" && Utils.likely_helm_chart?(f)
|
86
100
|
true
|
87
101
|
else
|
88
102
|
# This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
|
89
103
|
# in a multi-resource file had better be a valid k8s resource
|
90
|
-
content = ::YAML.safe_load(f.content, aliases: true)
|
104
|
+
content = ::YAML.safe_load(T.must(f.content), aliases: true)
|
91
105
|
likely_kubernetes_resource?(content)
|
92
106
|
end
|
93
107
|
rescue ::Psych::Exception
|
@@ -95,8 +109,9 @@ module Dependabot
|
|
95
109
|
end
|
96
110
|
end
|
97
111
|
|
112
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
98
113
|
def incorrectly_encoded_yamlfiles
|
99
|
-
yamlfiles.reject { |f| f.content
|
114
|
+
yamlfiles.reject { |f| f.content&.valid_encoding? }
|
100
115
|
end
|
101
116
|
end
|
102
117
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "docker_registry2"
|
@@ -7,10 +7,13 @@ require "dependabot/dependency"
|
|
7
7
|
require "dependabot/file_parsers"
|
8
8
|
require "dependabot/file_parsers/base"
|
9
9
|
require "dependabot/errors"
|
10
|
+
require "sorbet-runtime"
|
10
11
|
|
11
12
|
module Dependabot
|
12
13
|
module Docker
|
13
14
|
class FileParser < Dependabot::FileParsers::Base
|
15
|
+
extend T::Sig
|
16
|
+
|
14
17
|
require "dependabot/file_parsers/base/dependency_set"
|
15
18
|
|
16
19
|
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
@@ -39,11 +42,12 @@ module Dependabot
|
|
39
42
|
|
40
43
|
IMAGE_SPEC = %r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?(?:@sha256:#{DIGEST})?#{NAME}?}x
|
41
44
|
|
45
|
+
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
42
46
|
def parse
|
43
47
|
dependency_set = DependencySet.new
|
44
48
|
|
45
49
|
dockerfiles.each do |dockerfile|
|
46
|
-
dockerfile.content.each_line do |line|
|
50
|
+
T.must(dockerfile.content).each_line do |line|
|
47
51
|
next unless FROM_LINE.match?(line)
|
48
52
|
|
49
53
|
parsed_from_line = T.must(FROM_LINE.match(line)).named_captures
|
@@ -75,15 +79,18 @@ module Dependabot
|
|
75
79
|
|
76
80
|
private
|
77
81
|
|
82
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
78
83
|
def dockerfiles
|
79
84
|
# The Docker file fetcher fetches Dockerfiles and yaml files. Reject yaml files.
|
80
85
|
dependency_files.reject { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
81
86
|
end
|
82
87
|
|
88
|
+
sig { params(parsed_from_line: T::Hash[String, T.nilable(String)]).returns(T.nilable(String)) }
|
83
89
|
def version_from(parsed_from_line)
|
84
90
|
parsed_from_line.fetch("tag") || parsed_from_line.fetch("digest")
|
85
91
|
end
|
86
92
|
|
93
|
+
sig { params(parsed_from_line: T::Hash[String, T.nilable(String)]).returns(T::Hash[String, T.nilable(String)]) }
|
87
94
|
def source_from(parsed_from_line)
|
88
95
|
source = {}
|
89
96
|
|
@@ -96,6 +103,7 @@ module Dependabot
|
|
96
103
|
source
|
97
104
|
end
|
98
105
|
|
106
|
+
sig { override.void }
|
99
107
|
def check_required_files
|
100
108
|
# Just check if there are any files at all.
|
101
109
|
return if dependency_files.any?
|
@@ -103,6 +111,7 @@ module Dependabot
|
|
103
111
|
raise "No Dockerfile!"
|
104
112
|
end
|
105
113
|
|
114
|
+
sig { params(file: T.untyped).returns(Dependabot::FileParsers::Base::DependencySet) }
|
106
115
|
def workfile_file_dependencies(file)
|
107
116
|
dependency_set = DependencySet.new
|
108
117
|
|
@@ -130,6 +139,10 @@ module Dependabot
|
|
130
139
|
raise Dependabot::DependencyFileNotParseable, file.path
|
131
140
|
end
|
132
141
|
|
142
|
+
sig do
|
143
|
+
params(file: T.untyped, details: T.untyped,
|
144
|
+
version: T.nilable(T.any(String, Dependabot::Version))).returns(Dependabot::Dependency)
|
145
|
+
end
|
133
146
|
def build_image_dependency(file, details, version)
|
134
147
|
Dependency.new(
|
135
148
|
name: details.fetch("image"),
|
@@ -144,6 +157,7 @@ module Dependabot
|
|
144
157
|
)
|
145
158
|
end
|
146
159
|
|
160
|
+
sig { params(json_obj: T.anything).returns(T.untyped) }
|
147
161
|
def deep_fetch_images(json_obj)
|
148
162
|
case json_obj
|
149
163
|
when Hash then deep_fetch_images_from_hash(json_obj)
|
@@ -152,6 +166,7 @@ module Dependabot
|
|
152
166
|
end
|
153
167
|
end
|
154
168
|
|
169
|
+
sig { params(json_object: T.untyped).returns(T::Array[T.untyped]) }
|
155
170
|
def deep_fetch_images_from_hash(json_object)
|
156
171
|
img = json_object.fetch("image", nil)
|
157
172
|
|
@@ -167,11 +182,13 @@ module Dependabot
|
|
167
182
|
images + json_object.values.flat_map { |obj| deep_fetch_images(obj) }
|
168
183
|
end
|
169
184
|
|
185
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
170
186
|
def manifest_files
|
171
187
|
# Dependencies include both Dockerfiles and yaml, select yaml.
|
172
188
|
dependency_files.select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
173
189
|
end
|
174
190
|
|
191
|
+
sig { params(img_hash: T::Hash[String, T.nilable(String)]).returns(T::Array[String]) }
|
175
192
|
def parse_helm(img_hash)
|
176
193
|
tag_value = img_hash.key?("tag") ? img_hash.fetch("tag", nil) : img_hash.fetch("version", nil)
|
177
194
|
return [] unless tag_value
|
@@ -179,7 +196,7 @@ module Dependabot
|
|
179
196
|
repo = img_hash.fetch("repository", nil)
|
180
197
|
return [] unless repo
|
181
198
|
|
182
|
-
tag_details = tag_value.to_s.match(TAG_WITH_DIGEST).named_captures
|
199
|
+
tag_details = T.must(tag_value.to_s.match(TAG_WITH_DIGEST)).named_captures
|
183
200
|
tag = tag_details["tag"]
|
184
201
|
return [repo] unless tag
|
185
202
|
|
@@ -1,19 +1,23 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/docker/utils/helpers"
|
5
5
|
require "dependabot/file_updaters"
|
6
6
|
require "dependabot/file_updaters/base"
|
7
7
|
require "dependabot/errors"
|
8
|
+
require "sorbet-runtime"
|
8
9
|
|
9
10
|
module Dependabot
|
10
11
|
module Docker
|
11
12
|
class FileUpdater < Dependabot::FileUpdaters::Base
|
13
|
+
extend T::Sig
|
14
|
+
|
12
15
|
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i
|
13
16
|
|
14
17
|
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
15
18
|
DOCKER_REGEXP = /dockerfile/i
|
16
19
|
|
20
|
+
sig { override.returns(T::Array[Regexp]) }
|
17
21
|
def self.updated_files_regex
|
18
22
|
[
|
19
23
|
DOCKER_REGEXP,
|
@@ -21,15 +25,16 @@ module Dependabot
|
|
21
25
|
]
|
22
26
|
end
|
23
27
|
|
28
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
24
29
|
def updated_dependency_files
|
25
30
|
updated_files = []
|
26
31
|
dependency_files.each do |file|
|
27
|
-
next unless requirement_changed?(file, dependency)
|
32
|
+
next unless requirement_changed?(file, T.must(dependency))
|
28
33
|
|
29
34
|
updated_files << if file.name.match?(YAML_REGEXP)
|
30
35
|
updated_file(
|
31
36
|
file: file,
|
32
|
-
content: updated_yaml_content(file)
|
37
|
+
content: T.must(updated_yaml_content(file))
|
33
38
|
)
|
34
39
|
else
|
35
40
|
updated_file(
|
@@ -47,11 +52,13 @@ module Dependabot
|
|
47
52
|
|
48
53
|
private
|
49
54
|
|
55
|
+
sig { returns T.nilable(Dependabot::Dependency) }
|
50
56
|
def dependency
|
51
57
|
# Dockerfiles will only ever be updating a single dependency
|
52
58
|
dependencies.first
|
53
59
|
end
|
54
60
|
|
61
|
+
sig { override.void }
|
55
62
|
def check_required_files
|
56
63
|
# Just check if there are any files at all.
|
57
64
|
return if dependency_files.any?
|
@@ -59,14 +66,15 @@ module Dependabot
|
|
59
66
|
raise "No Dockerfile!"
|
60
67
|
end
|
61
68
|
|
69
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
62
70
|
def updated_dockerfile_content(file)
|
63
71
|
old_sources = previous_sources(file)
|
64
72
|
new_sources = sources(file)
|
65
73
|
|
66
|
-
updated_content = file.content
|
74
|
+
updated_content = T.let(file.content, T.untyped)
|
67
75
|
|
68
|
-
old_sources.zip(new_sources).each do |old_source, new_source|
|
69
|
-
updated_content = update_digest_and_tag(updated_content, old_source, new_source)
|
76
|
+
T.must(old_sources).zip(new_sources).each do |old_source, new_source|
|
77
|
+
updated_content = update_digest_and_tag(updated_content, old_source, T.must(new_source))
|
70
78
|
end
|
71
79
|
|
72
80
|
raise "Expected content to change!" if updated_content == file.content
|
@@ -74,6 +82,10 @@ module Dependabot
|
|
74
82
|
updated_content
|
75
83
|
end
|
76
84
|
|
85
|
+
sig do
|
86
|
+
params(previous_content: String, old_source: T::Hash[Symbol, T.nilable(String)],
|
87
|
+
new_source: T::Hash[Symbol, T.nilable(String)]).returns(String)
|
88
|
+
end
|
77
89
|
def update_digest_and_tag(previous_content, old_source, new_source)
|
78
90
|
old_digest = old_source[:digest]
|
79
91
|
new_digest = new_source[:digest]
|
@@ -86,7 +98,7 @@ module Dependabot
|
|
86
98
|
else
|
87
99
|
""
|
88
100
|
end
|
89
|
-
old_declaration += dependency.name
|
101
|
+
old_declaration += T.must(dependency).name
|
90
102
|
old_declaration +=
|
91
103
|
if specified_with_tag?(old_source) then ":#{old_tag}"
|
92
104
|
else
|
@@ -109,36 +121,44 @@ module Dependabot
|
|
109
121
|
end
|
110
122
|
end
|
111
123
|
|
124
|
+
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
|
112
125
|
def specified_with_tag?(source)
|
113
126
|
source[:tag]
|
114
127
|
end
|
115
128
|
|
129
|
+
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
|
116
130
|
def specified_with_digest?(source)
|
117
131
|
source[:digest]
|
118
132
|
end
|
119
133
|
|
134
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[String])) }
|
120
135
|
def new_tags(file)
|
121
136
|
requirements(file)
|
122
137
|
.map { |r| r.fetch(:source)[:tag] }
|
123
138
|
end
|
124
139
|
|
140
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[String])) }
|
125
141
|
def old_tags(file)
|
126
142
|
previous_requirements(file)
|
127
|
-
|
143
|
+
&.map { |r| r.fetch(:source)[:tag] }
|
128
144
|
end
|
129
145
|
|
146
|
+
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
|
130
147
|
def private_registry_url(source)
|
131
148
|
source[:registry]
|
132
149
|
end
|
133
150
|
|
151
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, T.nilable(String)]]) }
|
134
152
|
def sources(file)
|
135
153
|
requirements(file).map { |r| r.fetch(:source) }
|
136
154
|
end
|
137
155
|
|
156
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[T::Hash[Symbol, T.nilable(String)]])) }
|
138
157
|
def previous_sources(file)
|
139
|
-
previous_requirements(file)
|
158
|
+
previous_requirements(file)&.map { |r| r.fetch(:source) }
|
140
159
|
end
|
141
160
|
|
161
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
142
162
|
def updated_yaml_content(file)
|
143
163
|
updated_content = file.content
|
144
164
|
updated_content = update_helm(file, updated_content) if Utils.likely_helm_chart?(file)
|
@@ -149,6 +169,7 @@ module Dependabot
|
|
149
169
|
updated_content
|
150
170
|
end
|
151
171
|
|
172
|
+
sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
|
152
173
|
def update_helm(file, content)
|
153
174
|
# TODO: this won't work if two images have the same tag version
|
154
175
|
old_tags = old_helm_tags(file)
|
@@ -158,13 +179,14 @@ module Dependabot
|
|
158
179
|
|
159
180
|
old_tags.each do |old_tag|
|
160
181
|
old_tag_regex = /^\s+(?:-\s)?(?:tag|version):\s+["']?#{old_tag}["']?(?=\s|$)/
|
161
|
-
modified_content = modified_content
|
182
|
+
modified_content = modified_content&.gsub(old_tag_regex) do |old_img_tag|
|
162
183
|
old_img_tag.gsub(old_tag.to_s, new_helm_tag(file).to_s)
|
163
184
|
end
|
164
185
|
end
|
165
186
|
modified_content
|
166
187
|
end
|
167
188
|
|
189
|
+
sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
|
168
190
|
def update_image(file, content)
|
169
191
|
old_images = old_yaml_images(file)
|
170
192
|
return if old_images.empty?
|
@@ -173,53 +195,59 @@ module Dependabot
|
|
173
195
|
|
174
196
|
old_images.each do |old_image|
|
175
197
|
old_image_regex = /^\s*(?:-\s)?image:\s+#{old_image}(?=\s|$)/
|
176
|
-
modified_content = modified_content
|
198
|
+
modified_content = modified_content&.gsub(old_image_regex) do |old_img|
|
177
199
|
old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
|
178
200
|
end
|
179
201
|
end
|
180
202
|
modified_content
|
181
203
|
end
|
182
204
|
|
205
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
183
206
|
def new_yaml_image(file)
|
184
|
-
element = dependency.requirements.find { |r| r[:file] == file.name }
|
185
|
-
prefix = element
|
186
|
-
digest = element
|
187
|
-
tag = element
|
188
|
-
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
207
|
+
element = T.must(dependency).requirements.find { |r| r[:file] == file.name }
|
208
|
+
prefix = element&.dig(:source, :registry) ? "#{element.fetch(:source)[:registry]}/" : ""
|
209
|
+
digest = element&.dig(:source, :digest) ? "@sha256:#{element.fetch(:source)[:digest]}" : ""
|
210
|
+
tag = element&.dig(:source, :tag) ? ":#{element.fetch(:source)[:tag]}" : ""
|
211
|
+
"#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
|
189
212
|
end
|
190
213
|
|
214
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
191
215
|
def old_yaml_images(file)
|
192
|
-
previous_requirements(file).map do |r|
|
216
|
+
T.must(previous_requirements(file)).map do |r|
|
193
217
|
prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
|
194
218
|
digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
|
195
219
|
tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
|
196
|
-
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
220
|
+
"#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
|
197
221
|
end
|
198
222
|
end
|
199
223
|
|
224
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
200
225
|
def old_helm_tags(file)
|
201
|
-
previous_requirements(file).map do |r|
|
226
|
+
T.must(previous_requirements(file)).map do |r|
|
202
227
|
tag = r.fetch(:source)[:tag] || ""
|
203
228
|
digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
|
204
229
|
"#{tag}#{digest}"
|
205
230
|
end
|
206
231
|
end
|
207
232
|
|
233
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
208
234
|
def new_helm_tag(file)
|
209
|
-
element = dependency.requirements.find { |r| r[:file] == file.name }
|
210
|
-
tag = element.
|
211
|
-
digest = element.
|
235
|
+
element = T.must(dependency).requirements.find { |r| r[:file] == file.name }
|
236
|
+
tag = T.must(element).dig(:source, :tag) || ""
|
237
|
+
digest = T.must(element).dig(:source, :digest) ? "@sha256:#{T.must(element).dig(:source, :digest)}" : ""
|
212
238
|
"#{tag}#{digest}"
|
213
239
|
end
|
214
240
|
|
241
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
215
242
|
def requirements(file)
|
216
|
-
dependency.requirements
|
217
|
-
|
243
|
+
T.must(dependency).requirements
|
244
|
+
.select { |r| r[:file] == file.name }
|
218
245
|
end
|
219
246
|
|
247
|
+
sig { params(file: Dependabot::DependencyFile).returns T.nilable(T::Array[T::Hash[Symbol, T.untyped]]) }
|
220
248
|
def previous_requirements(file)
|
221
|
-
dependency.previous_requirements
|
222
|
-
|
249
|
+
T.must(dependency).previous_requirements
|
250
|
+
&.select { |r| r[:file] == file.name }
|
223
251
|
end
|
224
252
|
end
|
225
253
|
end
|
@@ -1,15 +1,19 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/metadata_finders"
|
5
5
|
require "dependabot/metadata_finders/base"
|
6
6
|
require "dependabot/shared_helpers"
|
7
|
+
require "sorbet-runtime"
|
7
8
|
|
8
9
|
module Dependabot
|
9
10
|
module Docker
|
10
11
|
class MetadataFinder < Dependabot::MetadataFinders::Base
|
12
|
+
extend T::Sig
|
13
|
+
|
11
14
|
private
|
12
15
|
|
16
|
+
sig { override.returns(T.nilable(Dependabot::Source)) }
|
13
17
|
def look_up_source
|
14
18
|
return if dependency.requirements.empty?
|
15
19
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "sorbet-runtime"
|
@@ -17,15 +17,17 @@ module Dependabot
|
|
17
17
|
# contains a single element.
|
18
18
|
sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
|
19
19
|
def self.requirements_array(requirement_string)
|
20
|
-
[new(requirement_string)]
|
20
|
+
[new(T.must(requirement_string))]
|
21
21
|
end
|
22
22
|
|
23
|
+
sig { override.params(version: Version).returns(T::Boolean) }
|
23
24
|
def satisfied_by?(version)
|
24
25
|
super(version.release_part)
|
25
26
|
end
|
26
27
|
|
27
28
|
# Patches Gem::Requirement to make it accept requirement strings like
|
28
29
|
# "~> 4.2.5, >= 4.2.5.1" without first needing to split them.
|
30
|
+
sig { params(requirements: String).void }
|
29
31
|
def initialize(*requirements)
|
30
32
|
requirements = requirements.flatten.flat_map do |req_string|
|
31
33
|
req_string.split(",").map(&:strip)
|
@@ -1,11 +1,13 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/docker/file_parser"
|
5
|
+
require "sorbet-runtime"
|
5
6
|
|
6
7
|
module Dependabot
|
7
8
|
module Docker
|
8
9
|
class Tag
|
10
|
+
extend T::Sig
|
9
11
|
WORDS_WITH_BUILD = /(?:(?:-[a-z]+)+-[0-9]+)+/
|
10
12
|
VERSION_REGEX = /v?(?<version>[0-9]+(?:\.[0-9]+)*(?:_[0-9]+|\.[a-z0-9]+|#{WORDS_WITH_BUILD}|-(?:kb)?[0-9]+)*)/i
|
11
13
|
VERSION_WITH_SFX = /^#{VERSION_REGEX}(?<suffix>-[a-z][a-z0-9.\-]*)?$/i
|
@@ -17,25 +19,30 @@ module Dependabot
|
|
17
19
|
#{VERSION_WITH_SFX}|
|
18
20
|
#{VERSION_WITH_PFX_AND_SFX}
|
19
21
|
/x
|
20
|
-
|
22
|
+
sig { returns(String) }
|
21
23
|
attr_reader :name
|
22
24
|
|
25
|
+
sig { params(name: String).void }
|
23
26
|
def initialize(name)
|
24
27
|
@name = name
|
25
28
|
end
|
26
29
|
|
30
|
+
sig { returns(String) }
|
27
31
|
def to_s
|
28
32
|
name
|
29
33
|
end
|
30
34
|
|
35
|
+
sig { returns(T::Boolean) }
|
31
36
|
def digest?
|
32
37
|
name.match?(FileParser::DIGEST)
|
33
38
|
end
|
34
39
|
|
40
|
+
sig { returns(T.nilable(T::Boolean)) }
|
35
41
|
def looks_like_prerelease?
|
36
|
-
numeric_version
|
42
|
+
numeric_version&.match?(/[a-zA-Z]/)
|
37
43
|
end
|
38
44
|
|
45
|
+
sig { params(other: Tag).returns(T::Boolean) }
|
39
46
|
def comparable_to?(other)
|
40
47
|
return false unless comparable?
|
41
48
|
|
@@ -51,47 +58,55 @@ module Dependabot
|
|
51
58
|
equal_prefix && equal_format && equal_suffix
|
52
59
|
end
|
53
60
|
|
61
|
+
sig { returns(T::Boolean) }
|
54
62
|
def comparable?
|
55
63
|
name.match?(NAME_WITH_VERSION)
|
56
64
|
end
|
57
65
|
|
66
|
+
sig { params(other: Tag).returns(T::Boolean) }
|
58
67
|
def same_precision?(other)
|
59
68
|
other.precision == precision
|
60
69
|
end
|
61
70
|
|
71
|
+
sig { params(other: Tag).returns(T::Boolean) }
|
62
72
|
def same_but_less_precise?(other)
|
63
73
|
other.segments.zip(segments).all? do |segment, other_segment|
|
64
74
|
segment == other_segment || other_segment.nil?
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
78
|
+
sig { returns(T.nilable(T::Boolean)) }
|
68
79
|
def canonical?
|
69
80
|
return false unless numeric_version
|
70
81
|
return true if name == numeric_version
|
71
82
|
|
72
83
|
# .NET tags are suffixed with -sdk
|
73
|
-
return true if name == numeric_version + "-sdk"
|
84
|
+
return true if numeric_version && name == numeric_version.to_s + "-sdk"
|
74
85
|
|
75
|
-
name == "jdk-" + numeric_version
|
86
|
+
numeric_version && name == "jdk-" + T.must(numeric_version)
|
76
87
|
end
|
77
88
|
|
89
|
+
sig { returns T.nilable(String) }
|
78
90
|
def prefix
|
79
|
-
name.match(NAME_WITH_VERSION)
|
91
|
+
name.match(NAME_WITH_VERSION)&.named_captures&.fetch("prefix")
|
80
92
|
end
|
81
93
|
|
94
|
+
sig { returns T.nilable(String) }
|
82
95
|
def suffix
|
83
|
-
name.match(NAME_WITH_VERSION)
|
96
|
+
name.match(NAME_WITH_VERSION)&.named_captures&.fetch("suffix")
|
84
97
|
end
|
85
98
|
|
99
|
+
sig { returns T.nilable(String) }
|
86
100
|
def version
|
87
|
-
name.match(NAME_WITH_VERSION)
|
101
|
+
name.match(NAME_WITH_VERSION)&.named_captures&.fetch("version")
|
88
102
|
end
|
89
103
|
|
104
|
+
sig { returns(Symbol) }
|
90
105
|
def format
|
91
106
|
return :sha_suffixed if name.match?(/(^|\-g?)[0-9a-f]{7,}$/)
|
92
|
-
return :year_month if version
|
93
|
-
return :year_month_day if version
|
94
|
-
return :build_num if version
|
107
|
+
return :year_month if version&.match?(/^[12]\d{3}(?:[.\-]|$)/)
|
108
|
+
return :year_month_day if version&.match?(/^[12](?:\d{5}|\d{7})(?:[.\-]|$)/)
|
109
|
+
return :build_num if version&.match?(/^\d+$/)
|
95
110
|
|
96
111
|
# As an example, "21-ea-32", "22-ea-7", and "22-ea-jdk-nanoserver-1809"
|
97
112
|
# are mapped to "<version>-ea-<build_num>", "<version>-ea-<build_num>",
|
@@ -100,25 +115,28 @@ module Dependabot
|
|
100
115
|
# That means only "22-ea-7" will be considered as a viable update
|
101
116
|
# candidate for "21-ea-32", since it's the only one that respects that
|
102
117
|
# format.
|
103
|
-
if version
|
104
|
-
return :"<version>#{version.match(WORDS_WITH_BUILD).to_s.gsub(/-[0-9]+/, '-<build_num>')}"
|
118
|
+
if version&.match?(WORDS_WITH_BUILD)
|
119
|
+
return :"<version>#{T.must(version).match(WORDS_WITH_BUILD).to_s.gsub(/-[0-9]+/, '-<build_num>')}"
|
105
120
|
end
|
106
121
|
|
107
122
|
:normal
|
108
123
|
end
|
109
124
|
|
125
|
+
sig { returns(T.nilable(String)) }
|
110
126
|
def numeric_version
|
111
127
|
return unless comparable?
|
112
128
|
|
113
|
-
version
|
129
|
+
version&.gsub(/kb/i, "")&.gsub(/-[a-z]+/, "")&.downcase
|
114
130
|
end
|
115
131
|
|
132
|
+
sig { returns(Integer) }
|
116
133
|
def precision
|
117
134
|
segments.length
|
118
135
|
end
|
119
136
|
|
137
|
+
sig { returns(T::Array[String]) }
|
120
138
|
def segments
|
121
|
-
numeric_version.split(/[.-]/)
|
139
|
+
T.must(numeric_version).split(/[.-]/)
|
122
140
|
end
|
123
141
|
end
|
124
142
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "aws-sdk-ecr"
|
5
5
|
require "base64"
|
6
|
+
require "sorbet-runtime"
|
6
7
|
|
7
8
|
require "dependabot/credential"
|
8
9
|
require "dependabot/errors"
|
@@ -16,6 +17,7 @@ module Dependabot
|
|
16
17
|
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/
|
17
18
|
DEFAULT_DOCKER_HUB_REGISTRY = "registry.hub.docker.com"
|
18
19
|
|
20
|
+
sig { params(credentials: T::Array[Dependabot::Credential]).void }
|
19
21
|
def initialize(credentials)
|
20
22
|
@credentials = credentials
|
21
23
|
end
|
@@ -32,14 +34,20 @@ module Dependabot
|
|
32
34
|
build_aws_credentials(registry_details)
|
33
35
|
end
|
34
36
|
|
37
|
+
sig { returns(T.nilable(String)) }
|
35
38
|
def base_registry
|
36
|
-
@base_registry ||=
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
@base_registry ||= T.let(
|
40
|
+
credentials.find do |cred|
|
41
|
+
cred["type"] == "docker_registry" && cred.replaces_base?
|
42
|
+
end,
|
43
|
+
T.nilable(Dependabot::Credential)
|
44
|
+
)
|
45
|
+
@base_registry ||= Dependabot::Credential.new({ "registry" => DEFAULT_DOCKER_HUB_REGISTRY,
|
46
|
+
"credentials" => nil })
|
40
47
|
@base_registry["registry"]
|
41
48
|
end
|
42
49
|
|
50
|
+
sig { params(registry: String).returns(T::Boolean) }
|
43
51
|
def using_dockerhub?(registry)
|
44
52
|
registry == DEFAULT_DOCKER_HUB_REGISTRY
|
45
53
|
end
|
@@ -76,11 +84,11 @@ module Dependabot
|
|
76
84
|
|
77
85
|
# Otherwise, we need to use the provided Access Key ID and secret to
|
78
86
|
# generate a temporary username and password
|
79
|
-
@authorization_tokens ||= {}
|
87
|
+
@authorization_tokens ||= T.let({}, T.nilable(T::Hash[String, String]))
|
80
88
|
@authorization_tokens[registry_hostname] ||=
|
81
89
|
ecr_client.get_authorization_token.authorization_data.first.authorization_token
|
82
90
|
username, password =
|
83
|
-
Base64.decode64(@authorization_tokens[registry_hostname]).split(":")
|
91
|
+
Base64.decode64(T.must(@authorization_tokens[registry_hostname])).split(":")
|
84
92
|
registry_details.merge(Dependabot::Credential.new({ "username" => username, "password" => password }))
|
85
93
|
rescue Aws::Errors::MissingCredentialsError,
|
86
94
|
Aws::ECR::Errors::UnrecognizedClientException,
|
@@ -1,11 +1,16 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
4
6
|
module Dependabot
|
5
7
|
module Docker
|
6
8
|
module Utils
|
7
9
|
HELM_REGEXP = /values[\-a-zA-Z_0-9]*\.ya?ml$/i
|
8
10
|
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Boolean) }
|
9
14
|
def self.likely_helm_chart?(file)
|
10
15
|
file.name.match?(HELM_REGEXP)
|
11
16
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "dependabot/version"
|
5
5
|
require "dependabot/utils"
|
6
6
|
require "dependabot/docker/tag"
|
7
|
+
require "sorbet-runtime"
|
7
8
|
|
8
9
|
module Dependabot
|
9
10
|
module Docker
|
@@ -13,54 +14,65 @@ module Dependabot
|
|
13
14
|
# for a description of Java versions.
|
14
15
|
#
|
15
16
|
class Version < Dependabot::Version
|
17
|
+
extend T::Sig
|
16
18
|
# The regex has limits for the 0,255 and 1,255 repetitions to avoid infinite limits which makes codeql angry.
|
17
19
|
# A docker image cannot be longer than 255 characters anyways.
|
18
20
|
DOCKER_VERSION_REGEX = /^(?<prefix>[a-z._\-]{0,255})[_\-v]?(?<version>.{1,255})$/
|
19
21
|
|
22
|
+
sig { override.params(version: VersionParameter).void }
|
20
23
|
def initialize(version)
|
21
|
-
parsed_version = version.match(DOCKER_VERSION_REGEX)
|
22
|
-
release_part, update_part = parsed_version[:version].split("_", 2)
|
24
|
+
parsed_version = version.to_s.match(DOCKER_VERSION_REGEX)
|
25
|
+
release_part, update_part = T.must(T.must(parsed_version)[:version]).split("_", 2)
|
23
26
|
|
24
27
|
# The numeric_version is needed here to validate the version string (ex: 20.9.0-alpine3.18)
|
25
28
|
# when the call is made via Depenedabot Api to convert the image version to semver.
|
26
|
-
release_part = Tag.new(release_part.chomp(".").chomp("-").chomp("_")).numeric_version
|
29
|
+
release_part = Tag.new(T.must(release_part).chomp(".").chomp("-").chomp("_")).numeric_version
|
27
30
|
|
28
|
-
@release_part = Dependabot::Version.new(release_part.tr("-", "."))
|
29
|
-
@update_part =
|
31
|
+
@release_part = T.let(Dependabot::Version.new(T.must(release_part).tr("-", ".")), Dependabot::Version)
|
32
|
+
@update_part = T.let(
|
33
|
+
Dependabot::Version.new(update_part&.start_with?(/[0-9]/) ? update_part : 0),
|
34
|
+
Dependabot::Version
|
35
|
+
)
|
30
36
|
|
31
37
|
super(@release_part)
|
32
38
|
end
|
33
39
|
|
40
|
+
sig { override.params(version: VersionParameter).returns(T::Boolean) }
|
34
41
|
def self.correct?(version)
|
35
42
|
return true if version.is_a?(Gem::Version)
|
36
43
|
|
37
44
|
# We can't call new here because Gem::Version calls self.correct? in its initialize method
|
38
45
|
# causing an infinite loop, so instead we check if the release_part of the version is correct
|
39
|
-
parsed_version = version.match(DOCKER_VERSION_REGEX)
|
46
|
+
parsed_version = version.to_s.match(DOCKER_VERSION_REGEX)
|
40
47
|
return false if parsed_version.nil?
|
41
48
|
|
42
|
-
release_part, = parsed_version[:version].split("_", 2)
|
43
|
-
release_part = Tag.new(release_part.chomp(".").chomp("-").chomp("_")).numeric_version || parsed_version
|
49
|
+
release_part, = T.must(parsed_version[:version]).split("_", 2)
|
50
|
+
release_part = Tag.new(T.must(release_part).chomp(".").chomp("-").chomp("_")).numeric_version || parsed_version
|
44
51
|
super(release_part.to_s)
|
45
52
|
rescue ArgumentError
|
46
53
|
# if we can't instantiate a version, it can't be correct
|
47
54
|
false
|
48
55
|
end
|
49
56
|
|
57
|
+
sig { override.returns(String) }
|
50
58
|
def to_semver
|
51
59
|
@release_part.to_semver
|
52
60
|
end
|
53
61
|
|
62
|
+
sig { returns(T::Array[String]) }
|
54
63
|
def segments
|
55
64
|
@release_part.segments
|
56
65
|
end
|
57
66
|
|
67
|
+
sig { returns(Dependabot::Version) }
|
58
68
|
attr_reader :release_part
|
59
69
|
|
70
|
+
sig { params(other: Dependabot::Docker::Version).returns(T.nilable(Integer)) }
|
60
71
|
def <=>(other)
|
61
72
|
sort_criteria <=> other.sort_criteria
|
62
73
|
end
|
63
74
|
|
75
|
+
sig { returns(T::Array[Dependabot::Version]) }
|
64
76
|
def sort_criteria
|
65
77
|
[@release_part, @update_part]
|
66
78
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-docker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.260.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dependabot-common
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.260.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.260.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -258,7 +258,7 @@ licenses:
|
|
258
258
|
- MIT
|
259
259
|
metadata:
|
260
260
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
261
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
261
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.260.0
|
262
262
|
post_install_message:
|
263
263
|
rdoc_options: []
|
264
264
|
require_paths:
|