dependabot-docker_compose 0.298.0 → 0.299.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dependabot/docker_compose/file_fetcher.rb +1 -1
- data/lib/dependabot/docker_compose/file_parser.rb +44 -3
- data/lib/dependabot/docker_compose/file_updater.rb +2 -44
- data/lib/dependabot/docker_compose/package_manager.rb +0 -2
- data/lib/dependabot/docker_compose.rb +8 -4
- metadata +19 -15
- data/lib/dependabot/docker_compose/metadata_finder.rb +0 -39
- data/lib/dependabot/docker_compose/requirement.rb +0 -43
- data/lib/dependabot/docker_compose/tag.rb +0 -144
- data/lib/dependabot/docker_compose/update_checker.rb +0 -479
- data/lib/dependabot/docker_compose/version.rb +0 -84
- data/lib/dependabot/shared/shared_file_fetcher.rb +0 -99
- data/lib/dependabot/shared/shared_file_parser.rb +0 -80
- data/lib/dependabot/shared/shared_file_updater.rb +0 -261
- data/lib/dependabot/shared/utils/credentials_finder.rb +0 -101
- data/lib/dependabot/shared/utils/helpers.rb +0 -19
@@ -1,479 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "docker_registry2"
|
5
|
-
require "sorbet-runtime"
|
6
|
-
|
7
|
-
require "dependabot/update_checkers"
|
8
|
-
require "dependabot/update_checkers/base"
|
9
|
-
require "dependabot/errors"
|
10
|
-
require "dependabot/docker_compose/tag"
|
11
|
-
require "dependabot/docker_compose/file_parser"
|
12
|
-
require "dependabot/docker_compose/version"
|
13
|
-
require "dependabot/docker_compose/requirement"
|
14
|
-
require "dependabot/shared/utils/credentials_finder"
|
15
|
-
|
16
|
-
module Dependabot
|
17
|
-
module DockerCompose
|
18
|
-
# rubocop:disable Metrics/ClassLength
|
19
|
-
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
20
|
-
extend T::Sig
|
21
|
-
|
22
|
-
sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
|
23
|
-
def latest_version
|
24
|
-
latest_version_from(T.must(dependency.version))
|
25
|
-
end
|
26
|
-
|
27
|
-
sig { override.returns(T.nilable(T.any(String, Gem::Version))) }
|
28
|
-
def latest_resolvable_version
|
29
|
-
# Resolvability isn't an issue for Docker containers.
|
30
|
-
latest_version
|
31
|
-
end
|
32
|
-
|
33
|
-
sig { override.returns(T.nilable(String)) }
|
34
|
-
def latest_resolvable_version_with_no_unlock
|
35
|
-
# No concept of "unlocking" for Docker containers
|
36
|
-
dependency.version
|
37
|
-
end
|
38
|
-
|
39
|
-
sig { override.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
40
|
-
def updated_requirements
|
41
|
-
dependency.requirements.map do |req|
|
42
|
-
updated_source = req.fetch(:source).dup
|
43
|
-
|
44
|
-
tag = req[:source][:tag]
|
45
|
-
digest = req[:source][:digest]
|
46
|
-
|
47
|
-
if tag
|
48
|
-
updated_tag = latest_version_from(tag)
|
49
|
-
updated_source[:tag] = updated_tag
|
50
|
-
updated_source[:digest] = digest_of(updated_tag) if digest
|
51
|
-
elsif digest
|
52
|
-
updated_source[:digest] = digest_of("latest")
|
53
|
-
end
|
54
|
-
|
55
|
-
req.merge(source: updated_source)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
sig { override.returns(T::Boolean) }
|
62
|
-
def latest_version_resolvable_with_full_unlock?
|
63
|
-
# Full unlock checks aren't relevant for Dockerfiles
|
64
|
-
false
|
65
|
-
end
|
66
|
-
|
67
|
-
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
68
|
-
def updated_dependencies_after_full_unlock
|
69
|
-
raise NotImplementedError
|
70
|
-
end
|
71
|
-
|
72
|
-
sig { params(requirements_to_unlock: T.nilable(Symbol)).returns(T::Boolean) }
|
73
|
-
def version_can_update?(requirements_to_unlock:) # rubocop:disable Lint/UnusedMethodArgument
|
74
|
-
if digest_requirements.any?
|
75
|
-
!digest_up_to_date?
|
76
|
-
else
|
77
|
-
!version_up_to_date?
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
sig { returns(T::Boolean) }
|
82
|
-
def version_up_to_date?
|
83
|
-
if digest_requirements.any?
|
84
|
-
version_tag_up_to_date? && digest_up_to_date?
|
85
|
-
else
|
86
|
-
version_tag_up_to_date?
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
sig { returns(T::Boolean) }
|
91
|
-
def version_tag_up_to_date?
|
92
|
-
version = dependency.version
|
93
|
-
return false unless version
|
94
|
-
|
95
|
-
return true unless version_tag.comparable?
|
96
|
-
|
97
|
-
latest_tag = latest_tag_from(version)
|
98
|
-
|
99
|
-
comparable_version_from(latest_tag) <= comparable_version_from(version_tag)
|
100
|
-
end
|
101
|
-
|
102
|
-
sig { returns(T::Boolean) }
|
103
|
-
def digest_up_to_date?
|
104
|
-
digest_requirements.all? do |req|
|
105
|
-
next true unless updated_digest
|
106
|
-
|
107
|
-
req.fetch(:source).fetch(:digest) == updated_digest
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
sig { params(version: String).returns(String) }
|
112
|
-
def latest_version_from(version)
|
113
|
-
latest_tag_from(version).name
|
114
|
-
end
|
115
|
-
|
116
|
-
sig { params(version: String).returns(Dependabot::DockerCompose::Tag) }
|
117
|
-
def latest_tag_from(version)
|
118
|
-
@tags ||= T.let({}, T.nilable(T::Hash[String, Dependabot::DockerCompose::Tag]))
|
119
|
-
return T.must(@tags[version]) if @tags.key?(version)
|
120
|
-
|
121
|
-
@tags[version] = fetch_latest_tag(Tag.new(version))
|
122
|
-
end
|
123
|
-
|
124
|
-
# NOTE: It's important that this *always* returns a tag (even if
|
125
|
-
# it's the existing one) as it is what we later check the digest of.
|
126
|
-
sig { params(version_tag: Dependabot::DockerCompose::Tag).returns(Dependabot::DockerCompose::Tag) }
|
127
|
-
def fetch_latest_tag(version_tag)
|
128
|
-
return Tag.new(T.must(latest_digest)) if version_tag.digest? && latest_digest
|
129
|
-
return version_tag unless version_tag.comparable?
|
130
|
-
|
131
|
-
# Prune out any downgrade tags before checking for pre-releases
|
132
|
-
# (which requires a call to the registry for each tag, so can be slow)
|
133
|
-
candidate_tags = comparable_tags_from_registry(version_tag)
|
134
|
-
candidate_tags = remove_version_downgrades(candidate_tags, version_tag)
|
135
|
-
candidate_tags = remove_prereleases(candidate_tags, version_tag)
|
136
|
-
candidate_tags = filter_ignored(candidate_tags)
|
137
|
-
candidate_tags = sort_tags(candidate_tags, version_tag)
|
138
|
-
|
139
|
-
latest_tag = candidate_tags.last
|
140
|
-
return version_tag unless latest_tag
|
141
|
-
|
142
|
-
return latest_tag if latest_tag.same_precision?(version_tag)
|
143
|
-
|
144
|
-
latest_same_precision_tag = remove_precision_changes(candidate_tags, version_tag).last
|
145
|
-
return latest_tag unless latest_same_precision_tag
|
146
|
-
|
147
|
-
latest_same_precision_digest = digest_of(latest_same_precision_tag.name)
|
148
|
-
latest_digest = digest_of(latest_tag.name)
|
149
|
-
|
150
|
-
# NOTE: Some registries don't provide digests (the API documents them as
|
151
|
-
# optional: https://docs.docker.com/registry/spec/api/#content-digests).
|
152
|
-
#
|
153
|
-
# In that case we can't know for sure whether the latest tag keeping
|
154
|
-
# existing precision is the same as the absolute latest tag.
|
155
|
-
#
|
156
|
-
# We can however, make a best-effort to avoid unwanted changes by
|
157
|
-
# directly looking at version numbers and checking whether the absolute
|
158
|
-
# latest tag is just a more precise version of the latest tag that keeps
|
159
|
-
# existing precision.
|
160
|
-
|
161
|
-
if latest_same_precision_digest == latest_digest && latest_same_precision_tag.same_but_less_precise?(latest_tag)
|
162
|
-
latest_same_precision_tag
|
163
|
-
else
|
164
|
-
latest_tag
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
sig { params(original_tag: Dependabot::DockerCompose::Tag).returns(T::Array[Dependabot::DockerCompose::Tag]) }
|
169
|
-
def comparable_tags_from_registry(original_tag)
|
170
|
-
tags_from_registry.select { |tag| tag.comparable_to?(original_tag) }
|
171
|
-
end
|
172
|
-
|
173
|
-
sig do
|
174
|
-
params(
|
175
|
-
candidate_tags: T::Array[Dependabot::DockerCompose::Tag],
|
176
|
-
version_tag: Dependabot::DockerCompose::Tag
|
177
|
-
)
|
178
|
-
.returns(T::Array[Dependabot::DockerCompose::Tag])
|
179
|
-
end
|
180
|
-
def remove_version_downgrades(candidate_tags, version_tag)
|
181
|
-
current_version = comparable_version_from(version_tag)
|
182
|
-
|
183
|
-
candidate_tags.select do |tag|
|
184
|
-
comparable_version_from(tag) >= current_version
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
sig do
|
189
|
-
params(
|
190
|
-
candidate_tags: T::Array[Dependabot::DockerCompose::Tag],
|
191
|
-
version_tag: Dependabot::DockerCompose::Tag
|
192
|
-
)
|
193
|
-
.returns(T::Array[Dependabot::DockerCompose::Tag])
|
194
|
-
end
|
195
|
-
def remove_prereleases(candidate_tags, version_tag)
|
196
|
-
return candidate_tags if prerelease?(version_tag)
|
197
|
-
|
198
|
-
candidate_tags.reject { |tag| prerelease?(tag) }
|
199
|
-
end
|
200
|
-
|
201
|
-
sig do
|
202
|
-
params(
|
203
|
-
candidate_tags: T::Array[Dependabot::DockerCompose::Tag],
|
204
|
-
version_tag: Dependabot::DockerCompose::Tag
|
205
|
-
)
|
206
|
-
.returns(T::Array[Dependabot::DockerCompose::Tag])
|
207
|
-
end
|
208
|
-
def remove_precision_changes(candidate_tags, version_tag)
|
209
|
-
candidate_tags.select do |tag|
|
210
|
-
tag.same_precision?(version_tag)
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
sig { returns(T.nilable(Dependabot::DockerCompose::Tag)) }
|
215
|
-
def latest_tag
|
216
|
-
return unless latest_digest
|
217
|
-
|
218
|
-
tags_from_registry
|
219
|
-
.select(&:canonical?)
|
220
|
-
.sort_by { |t| comparable_version_from(t) }
|
221
|
-
.reverse
|
222
|
-
.find { |t| digest_of(t.name) == latest_digest }
|
223
|
-
end
|
224
|
-
|
225
|
-
sig { returns(T.nilable(String)) }
|
226
|
-
def updated_digest
|
227
|
-
@updated_digest ||= T.let(
|
228
|
-
if latest_tag_from(T.must(dependency.version)).digest?
|
229
|
-
latest_digest
|
230
|
-
else
|
231
|
-
digest_of(T.cast(latest_version, String))
|
232
|
-
end,
|
233
|
-
T.nilable(String)
|
234
|
-
)
|
235
|
-
end
|
236
|
-
|
237
|
-
sig { returns(T::Array[Dependabot::DockerCompose::Tag]) }
|
238
|
-
def tags_from_registry
|
239
|
-
@tags_from_registry ||= T.let(
|
240
|
-
begin
|
241
|
-
client = docker_registry_client
|
242
|
-
|
243
|
-
client.tags(docker_repo_name, auto_paginate: true).fetch("tags").map { |name| Tag.new(name) }
|
244
|
-
rescue *transient_docker_errors
|
245
|
-
attempt ||= 1
|
246
|
-
attempt += 1
|
247
|
-
raise if attempt > 3
|
248
|
-
|
249
|
-
retry
|
250
|
-
end,
|
251
|
-
T.nilable(T::Array[Dependabot::DockerCompose::Tag])
|
252
|
-
)
|
253
|
-
rescue DockerRegistry2::RegistryAuthenticationException,
|
254
|
-
RestClient::Forbidden
|
255
|
-
raise PrivateSourceAuthenticationFailure, registry_hostname
|
256
|
-
rescue RestClient::Exceptions::OpenTimeout,
|
257
|
-
RestClient::Exceptions::ReadTimeout
|
258
|
-
raise if using_dockerhub?
|
259
|
-
|
260
|
-
raise PrivateSourceTimedOut, T.must(registry_hostname)
|
261
|
-
rescue RestClient::ServerBrokeConnection,
|
262
|
-
RestClient::TooManyRequests
|
263
|
-
raise PrivateSourceBadResponse, registry_hostname
|
264
|
-
rescue JSON::ParserError => e
|
265
|
-
if e.message.include?("unexpected token")
|
266
|
-
raise DependencyFileNotResolvable, "Error while accessing docker image at #{registry_hostname}"
|
267
|
-
end
|
268
|
-
|
269
|
-
raise
|
270
|
-
end
|
271
|
-
|
272
|
-
sig { returns(T.nilable(String)) }
|
273
|
-
def latest_digest
|
274
|
-
return unless tags_from_registry.map(&:name).include?("latest")
|
275
|
-
|
276
|
-
digest_of("latest")
|
277
|
-
end
|
278
|
-
|
279
|
-
sig { params(tag: String).returns(T.nilable(String)) }
|
280
|
-
def digest_of(tag)
|
281
|
-
@digests ||= T.let({}, T.nilable(T::Hash[String, T.nilable(String)]))
|
282
|
-
return @digests[tag] if @digests.key?(tag)
|
283
|
-
|
284
|
-
@digests[tag] = fetch_digest_of(tag)
|
285
|
-
end
|
286
|
-
|
287
|
-
sig { params(tag: String).returns(T.nilable(String)) }
|
288
|
-
def fetch_digest_of(tag)
|
289
|
-
docker_registry_client.manifest_digest(docker_repo_name, tag)&.delete_prefix("sha256:")
|
290
|
-
rescue *transient_docker_errors => e
|
291
|
-
attempt ||= 1
|
292
|
-
attempt += 1
|
293
|
-
return if attempt > 3 && e.is_a?(DockerRegistry2::NotFound)
|
294
|
-
raise PrivateSourceBadResponse, registry_hostname if attempt > 3
|
295
|
-
|
296
|
-
retry
|
297
|
-
rescue DockerRegistry2::RegistryAuthenticationException,
|
298
|
-
RestClient::Forbidden
|
299
|
-
raise PrivateSourceAuthenticationFailure, registry_hostname
|
300
|
-
rescue RestClient::ServerBrokeConnection,
|
301
|
-
RestClient::TooManyRequests
|
302
|
-
raise PrivateSourceBadResponse, registry_hostname
|
303
|
-
rescue JSON::ParserError
|
304
|
-
Dependabot.logger.info \
|
305
|
-
"docker_registry_client.manifest_digest(#{docker_repo_name}, #{tag}) returned an empty string"
|
306
|
-
nil
|
307
|
-
end
|
308
|
-
|
309
|
-
sig { returns(T::Array[T.class_of(StandardError)]) }
|
310
|
-
def transient_docker_errors
|
311
|
-
[
|
312
|
-
RestClient::Exceptions::Timeout,
|
313
|
-
RestClient::ServerBrokeConnection,
|
314
|
-
RestClient::ServiceUnavailable,
|
315
|
-
RestClient::InternalServerError,
|
316
|
-
RestClient::BadGateway,
|
317
|
-
DockerRegistry2::NotFound
|
318
|
-
]
|
319
|
-
end
|
320
|
-
|
321
|
-
sig { params(tag: Dependabot::DockerCompose::Tag).returns(T::Boolean) }
|
322
|
-
def prerelease?(tag)
|
323
|
-
return true if tag.looks_like_prerelease?
|
324
|
-
|
325
|
-
# Compare the numeric version against the version of the `latest` tag.
|
326
|
-
return false unless latest_tag
|
327
|
-
|
328
|
-
if comparable_version_from(tag) > comparable_version_from(T.must(latest_tag))
|
329
|
-
Dependabot.logger.info \
|
330
|
-
"The `latest` tag points to the same image as the `#{T.must(latest_tag).name}` image, " \
|
331
|
-
"so dependabot is treating `#{tag.name}` as a pre-release. " \
|
332
|
-
"The `latest` tag needs to point to `#{tag.name}` for Dependabot to consider it."
|
333
|
-
|
334
|
-
true
|
335
|
-
else
|
336
|
-
false
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
sig { params(tag: Dependabot::DockerCompose::Tag).returns(Dependabot::Version) }
|
341
|
-
def comparable_version_from(tag)
|
342
|
-
version_class.new(tag.numeric_version)
|
343
|
-
end
|
344
|
-
|
345
|
-
sig { returns(T.nilable(String)) }
|
346
|
-
def registry_hostname
|
347
|
-
if dependency.requirements.first&.dig(:source, :registry)
|
348
|
-
return T.must(dependency.requirements.first).dig(:source, :registry)
|
349
|
-
end
|
350
|
-
|
351
|
-
credentials_finder.base_registry
|
352
|
-
end
|
353
|
-
|
354
|
-
sig { returns(T::Boolean) }
|
355
|
-
def using_dockerhub?
|
356
|
-
registry_hostname == "registry.hub.docker.com"
|
357
|
-
end
|
358
|
-
|
359
|
-
sig { returns(T.nilable(Dependabot::Credential)) }
|
360
|
-
def registry_credentials
|
361
|
-
credentials_finder.credentials_for_registry(registry_hostname)
|
362
|
-
end
|
363
|
-
|
364
|
-
sig { returns(Dependabot::Shared::Utils::CredentialsFinder) }
|
365
|
-
def credentials_finder
|
366
|
-
@credentials_finder ||= T.let(
|
367
|
-
Dependabot::Shared::Utils::CredentialsFinder.new(credentials),
|
368
|
-
T.nilable(Dependabot::Shared::Utils::CredentialsFinder)
|
369
|
-
)
|
370
|
-
end
|
371
|
-
|
372
|
-
sig { returns(String) }
|
373
|
-
def docker_repo_name
|
374
|
-
return dependency.name unless using_dockerhub?
|
375
|
-
return dependency.name unless dependency.name.split("/").count < 2
|
376
|
-
|
377
|
-
"library/#{dependency.name}"
|
378
|
-
end
|
379
|
-
|
380
|
-
# Defaults from https://github.com/deitch/docker_registry2/blob/bfde04144f0b7fd63c156a1aca83efe19ee78ffd/lib/registry/registry.rb#L26-L27
|
381
|
-
DEFAULT_DOCKER_OPEN_TIMEOUT_IN_SECONDS = 2
|
382
|
-
DEFAULT_DOCKER_READ_TIMEOUT_IN_SECONDS = 5
|
383
|
-
|
384
|
-
sig { returns(DockerRegistry2::Registry) }
|
385
|
-
def docker_registry_client
|
386
|
-
@docker_registry_client ||= T.let(
|
387
|
-
DockerRegistry2::Registry.new(
|
388
|
-
"https://#{registry_hostname}",
|
389
|
-
user: registry_credentials&.fetch("username", nil),
|
390
|
-
password: registry_credentials&.fetch("password", nil),
|
391
|
-
read_timeout: docker_read_timeout_in_seconds,
|
392
|
-
open_timeout: docker_open_timeout_in_seconds,
|
393
|
-
http_options: { proxy: ENV.fetch("HTTPS_PROXY", nil) }
|
394
|
-
),
|
395
|
-
T.nilable(DockerRegistry2::Registry)
|
396
|
-
)
|
397
|
-
end
|
398
|
-
|
399
|
-
sig { returns(Integer) }
|
400
|
-
def docker_open_timeout_in_seconds
|
401
|
-
ENV.fetch("DEPENDABOT_DOCKER_OPEN_TIMEOUT_IN_SECONDS", DEFAULT_DOCKER_OPEN_TIMEOUT_IN_SECONDS).to_i
|
402
|
-
end
|
403
|
-
|
404
|
-
sig { returns(Integer) }
|
405
|
-
def docker_read_timeout_in_seconds
|
406
|
-
ENV.fetch("DEPENDABOT_DOCKER_READ_TIMEOUT_IN_SECONDS", DEFAULT_DOCKER_READ_TIMEOUT_IN_SECONDS).to_i
|
407
|
-
end
|
408
|
-
|
409
|
-
sig do
|
410
|
-
params(
|
411
|
-
candidate_tags: T::Array[Dependabot::DockerCompose::Tag],
|
412
|
-
version_tag: Dependabot::DockerCompose::Tag
|
413
|
-
)
|
414
|
-
.returns(T::Array[Dependabot::DockerCompose::Tag])
|
415
|
-
end
|
416
|
-
def sort_tags(candidate_tags, version_tag)
|
417
|
-
candidate_tags.sort do |tag_a, tag_b|
|
418
|
-
if comparable_version_from(tag_a) > comparable_version_from(tag_b)
|
419
|
-
1
|
420
|
-
elsif comparable_version_from(tag_a) < comparable_version_from(tag_b)
|
421
|
-
-1
|
422
|
-
elsif tag_a.same_precision?(version_tag)
|
423
|
-
1
|
424
|
-
elsif tag_b.same_precision?(version_tag)
|
425
|
-
-1
|
426
|
-
else
|
427
|
-
0
|
428
|
-
end
|
429
|
-
end
|
430
|
-
end
|
431
|
-
|
432
|
-
sig do
|
433
|
-
params(candidate_tags: T::Array[Dependabot::DockerCompose::Tag])
|
434
|
-
.returns(T::Array[Dependabot::DockerCompose::Tag])
|
435
|
-
end
|
436
|
-
def filter_ignored(candidate_tags)
|
437
|
-
filtered =
|
438
|
-
candidate_tags
|
439
|
-
.reject do |tag|
|
440
|
-
version = comparable_version_from(tag)
|
441
|
-
ignore_requirements.any? { |r| r.satisfied_by?(version) }
|
442
|
-
end
|
443
|
-
if @raise_on_ignored &&
|
444
|
-
filter_lower_versions(filtered).empty? &&
|
445
|
-
filter_lower_versions(candidate_tags).any? &&
|
446
|
-
digest_requirements.none?
|
447
|
-
raise AllVersionsIgnored
|
448
|
-
end
|
449
|
-
|
450
|
-
filtered
|
451
|
-
end
|
452
|
-
|
453
|
-
sig { params(tags: T::Array[Dependabot::DockerCompose::Tag]).returns(T::Array[Dependabot::DockerCompose::Tag]) }
|
454
|
-
def filter_lower_versions(tags)
|
455
|
-
tags.select do |tag|
|
456
|
-
comparable_version_from(tag) > comparable_version_from(version_tag)
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
sig { returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
461
|
-
def digest_requirements
|
462
|
-
dependency.requirements.select do |requirement|
|
463
|
-
requirement.dig(:source, :digest)
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
sig { returns(Dependabot::DockerCompose::Tag) }
|
468
|
-
def version_tag
|
469
|
-
@version_tag ||= T.let(
|
470
|
-
Tag.new(T.must(dependency.version)),
|
471
|
-
T.nilable(Dependabot::DockerCompose::Tag)
|
472
|
-
)
|
473
|
-
end
|
474
|
-
end
|
475
|
-
# rubocop:enable Metrics/ClassLength
|
476
|
-
end
|
477
|
-
end
|
478
|
-
|
479
|
-
Dependabot::UpdateCheckers.register("docker_compose", Dependabot::DockerCompose::UpdateChecker)
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "dependabot/version"
|
5
|
-
require "dependabot/utils"
|
6
|
-
require "dependabot/docker_compose/tag"
|
7
|
-
require "sorbet-runtime"
|
8
|
-
|
9
|
-
module Dependabot
|
10
|
-
module DockerCompose
|
11
|
-
# In the special case of Java, the version string may also contain
|
12
|
-
# optional "update number" and "identifier" components.
|
13
|
-
# See https://www.oracle.com/java/technologies/javase/versioning-naming.html
|
14
|
-
# for a description of Java versions.
|
15
|
-
#
|
16
|
-
class Version < Dependabot::Version
|
17
|
-
extend T::Sig
|
18
|
-
# The regex has limits for the 0,255 and 1,255 repetitions to avoid infinite limits which makes codeql angry.
|
19
|
-
# A docker image cannot be longer than 255 characters anyways.
|
20
|
-
DOCKER_VERSION_REGEX = /^(?<prefix>[a-z._\-]{0,255})[_\-v]?(?<version>.{1,255})$/
|
21
|
-
|
22
|
-
sig { override.params(version: VersionParameter).void }
|
23
|
-
def initialize(version)
|
24
|
-
parsed_version = version.to_s.match(DOCKER_VERSION_REGEX)
|
25
|
-
release_part, update_part = T.must(T.must(parsed_version)[:version]).split("_", 2)
|
26
|
-
|
27
|
-
# The numeric_version is needed here to validate the version string (ex: 20.9.0-alpine3.18)
|
28
|
-
# when the call is made via Dependabot Api to convert the image version to semver.
|
29
|
-
release_part = Tag.new(T.must(release_part).chomp(".").chomp("-").chomp("_")).numeric_version
|
30
|
-
|
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
|
-
)
|
36
|
-
|
37
|
-
super(@release_part)
|
38
|
-
end
|
39
|
-
|
40
|
-
sig { override.params(version: VersionParameter).returns(T::Boolean) }
|
41
|
-
def self.correct?(version)
|
42
|
-
return true if version.is_a?(Gem::Version)
|
43
|
-
|
44
|
-
# We can't call new here because Gem::Version calls self.correct? in its initialize method
|
45
|
-
# causing an infinite loop, so instead we check if the release_part of the version is correct
|
46
|
-
parsed_version = version.to_s.match(DOCKER_VERSION_REGEX)
|
47
|
-
return false if parsed_version.nil?
|
48
|
-
|
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
|
51
|
-
super(release_part.to_s)
|
52
|
-
rescue ArgumentError
|
53
|
-
# if we can't instantiate a version, it can't be correct
|
54
|
-
false
|
55
|
-
end
|
56
|
-
|
57
|
-
sig { override.returns(String) }
|
58
|
-
def to_semver
|
59
|
-
@release_part.to_semver
|
60
|
-
end
|
61
|
-
|
62
|
-
sig { returns(T::Array[String]) }
|
63
|
-
def segments
|
64
|
-
@release_part.segments
|
65
|
-
end
|
66
|
-
|
67
|
-
sig { returns(Dependabot::Version) }
|
68
|
-
attr_reader :release_part
|
69
|
-
|
70
|
-
sig { params(other: Dependabot::DockerCompose::Version).returns(T.nilable(Integer)) }
|
71
|
-
def <=>(other)
|
72
|
-
sort_criteria <=> other.sort_criteria
|
73
|
-
end
|
74
|
-
|
75
|
-
sig { returns(T::Array[Dependabot::Version]) }
|
76
|
-
def sort_criteria
|
77
|
-
[@release_part, @update_part]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
Dependabot::Utils
|
84
|
-
.register_version_class("docker_compose", Dependabot::DockerCompose::Version)
|
@@ -1,99 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "sorbet-runtime"
|
5
|
-
require "dependabot/file_fetchers"
|
6
|
-
require "dependabot/file_fetchers/base"
|
7
|
-
require "dependabot/shared/utils/helpers"
|
8
|
-
|
9
|
-
module Dependabot
|
10
|
-
module Shared
|
11
|
-
class SharedFileFetcher < Dependabot::FileFetchers::Base
|
12
|
-
extend T::Sig
|
13
|
-
extend T::Helpers
|
14
|
-
|
15
|
-
abstract!
|
16
|
-
|
17
|
-
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
18
|
-
|
19
|
-
sig { abstract.returns(Regexp) }
|
20
|
-
def self.filename_regex; end
|
21
|
-
|
22
|
-
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
23
|
-
def self.required_files_in?(filenames)
|
24
|
-
filenames.any? { |f| f.match?(filename_regex) }
|
25
|
-
end
|
26
|
-
|
27
|
-
sig { override.returns(T::Array[DependencyFile]) }
|
28
|
-
def fetch_files
|
29
|
-
fetched_files = []
|
30
|
-
fetched_files + correctly_encoded_yamlfiles
|
31
|
-
end
|
32
|
-
|
33
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
34
|
-
def correctly_encoded_yamlfiles
|
35
|
-
candidate_files = yamlfiles.select { |f| f.content&.valid_encoding? }
|
36
|
-
candidate_files.select do |f|
|
37
|
-
if f.type == "file" && Utils.likely_helm_chart?(f)
|
38
|
-
true
|
39
|
-
else
|
40
|
-
# This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
|
41
|
-
# in a multi-resource file had better be a valid k8s resource
|
42
|
-
content = YAML.safe_load(T.must(f.content), aliases: true)
|
43
|
-
likely_kubernetes_resource?(content)
|
44
|
-
end
|
45
|
-
rescue ::Psych::Exception
|
46
|
-
false
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
51
|
-
def incorrectly_encoded_yamlfiles
|
52
|
-
yamlfiles.reject { |f| f.content&.valid_encoding? }
|
53
|
-
end
|
54
|
-
|
55
|
-
sig do
|
56
|
-
params(
|
57
|
-
incorrectly_encoded_files: T::Array[Dependabot::DependencyFile]
|
58
|
-
).returns(T.noreturn)
|
59
|
-
end
|
60
|
-
def raise_appropriate_error(
|
61
|
-
incorrectly_encoded_files = []
|
62
|
-
)
|
63
|
-
if incorrectly_encoded_files.none? && incorrectly_encoded_yamlfiles.none?
|
64
|
-
raise Dependabot::DependencyFileNotFound.new(
|
65
|
-
File.join(directory, "Dockerfile"),
|
66
|
-
"No Docker Compose manifest found in #{directory}"
|
67
|
-
)
|
68
|
-
end
|
69
|
-
|
70
|
-
invalid_files = incorrectly_encoded_files.any? ? incorrectly_encoded_files : incorrectly_encoded_yamlfiles
|
71
|
-
raise Dependabot::DependencyFileNotParseable, T.must(invalid_files.first).path
|
72
|
-
end
|
73
|
-
|
74
|
-
sig { returns(T::Array[DependencyFile]) }
|
75
|
-
def yamlfiles
|
76
|
-
@yamlfiles ||= T.let(
|
77
|
-
repo_contents(raise_errors: false)
|
78
|
-
.select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
79
|
-
.map { |f| fetch_file_from_host(f.name) },
|
80
|
-
T.nilable(T::Array[DependencyFile])
|
81
|
-
)
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
sig { params(resource: Object).returns(T.nilable(T::Boolean)) }
|
87
|
-
def likely_kubernetes_resource?(resource)
|
88
|
-
# Heuristic for being a Kubernetes resource. We could make this tighter but this probably works well.
|
89
|
-
resource.is_a?(::Hash) && resource.key?("apiVersion") && resource.key?("kind")
|
90
|
-
end
|
91
|
-
|
92
|
-
sig { abstract.returns(String) }
|
93
|
-
def default_file_name; end
|
94
|
-
|
95
|
-
sig { abstract.returns(String) }
|
96
|
-
def file_type; end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|