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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ded4ed623fee8809efe189b637e261bc4ffb2b3e9673432489c42ba318830011
4
- data.tar.gz: a87f37b18e0eb8fdcc186a46e41ef28fe6097c21cd1f1d3fc871a7386f47b745
3
+ metadata.gz: c2b8bc30ebbc60146a41cb00193a87642254cea63e48d63a6ceb5f0bd3305e63
4
+ data.tar.gz: 8085d1d31692622d6ee57bbdc0466e388257275fb7aaa3841a6a426192f9f5ea
5
5
  SHA512:
6
- metadata.gz: cb30e7ca42d906f02639c6d92d9ced36a9d1139fbda59456332c2718c70c9b2a439aa9e2f3b8d8bcc46fa718326820006c7d24ce884979846027ae4e9457d012
7
- data.tar.gz: 40bddffd6d37c1e33cb4b0d7ad130da57a3246bffa02e2e5b9321a85da9b6ac441c5749bf8a19995302cab44d3e939b27a32b1f894c0d1144a766ef525789234
6
+ metadata.gz: 95e886db37f7981efa16b2c00569b3eb37cb5dc5ebcb4824d34cb2d776d25276c982597088451d8cbdf2816fddf19418c0979c699e80529d5c63b5fef34dcbd2
7
+ data.tar.gz: c9034900857a4f7714769f3639da527f8cfdc2d70ab2591b54718e33844c70c76364c6cc8e808523988bbd7f78bb6a6c7c97fcacec14ed15b8b4ecd7290ace09
@@ -1,4 +1,4 @@
1
- # typed: true
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
- repo_contents(raise_errors: false)
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.valid_encoding? }
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.valid_encoding? }
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
- .select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
74
- .map { |f| fetch_file_from_host(f.name) }
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.valid_encoding? }
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.valid_encoding? }
114
+ yamlfiles.reject { |f| f.content&.valid_encoding? }
100
115
  end
101
116
  end
102
117
  end
@@ -1,4 +1,4 @@
1
- # typed: true
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: true
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
- .map { |r| r.fetch(:source)[:tag] }
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).map { |r| r.fetch(:source) }
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.gsub(old_tag_regex) do |old_img_tag|
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.gsub(old_image_regex) do |old_img|
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.fetch(:source)[:registry] ? "#{element.fetch(:source)[:registry]}/" : ""
186
- digest = element.fetch(:source)[:digest] ? "@sha256:#{element.fetch(:source)[:digest]}" : ""
187
- tag = element.fetch(:source)[:tag] ? ":#{element.fetch(:source)[:tag]}" : ""
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.fetch(:source)[:tag] || ""
211
- digest = element.fetch(:source)[:digest] ? "@sha256:#{element.fetch(:source)[:digest]}" : ""
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
- .select { |r| r[:file] == file.name }
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
- .select { |r| r[:file] == file.name }
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: true
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: true
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: true
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.match?(/[a-zA-Z]/)
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).named_captures.fetch("prefix")
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).named_captures.fetch("suffix")
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).named_captures.fetch("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.match?(/^[12]\d{3}(?:[.\-]|$)/)
93
- return :year_month_day if version.match?(/^[12](?:\d{5}|\d{7})(?:[.\-]|$)/)
94
- return :build_num if version.match?(/^\d+$/)
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.match?(WORDS_WITH_BUILD)
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.gsub(/kb/i, "").gsub(/-[a-z]+/, "").downcase
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
@@ -357,7 +357,7 @@ module Dependabot
357
357
  end
358
358
 
359
359
  def version_tag
360
- @version_tag ||= Tag.new(dependency.version)
360
+ @version_tag ||= Tag.new(T.must(dependency.version))
361
361
  end
362
362
  end
363
363
  end
@@ -1,8 +1,9 @@
1
- # typed: true
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 ||= credentials.find do |cred|
37
- cred["type"] == "docker_registry" && cred.replaces_base?
38
- end
39
- @base_registry ||= { "registry" => DEFAULT_DOCKER_HUB_REGISTRY, "credentials" => nil }
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: true
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: true
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 = Dependabot::Version.new(update_part&.start_with?(/[0-9]/) ? update_part : 0)
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.259.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-05-30 00:00:00.000000000 Z
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.259.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.259.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.259.0
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: