dependabot-docker 0.259.0 → 0.260.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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: