dependabot-docker 0.213.0 → 0.214.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: ebc54cfc835861f5ff7c37dabe72928e92786cda8925b7744029121b17fdc097
4
- data.tar.gz: 4e99d4753649e65f7d34295f596c9de8e1060487e066c8c3cc6a2054e9e6c30a
3
+ metadata.gz: b7aabb905dbb5a64638f936079c477b091dd8b2a05aad7cc070c10e11977cc97
4
+ data.tar.gz: 4cc6bbe0291ded5c2e975f2256561044700a6a5903f1d110815de0ff5a0da281
5
5
  SHA512:
6
- metadata.gz: 2312611afec9a4b58b82b0327a95f9257ae1cfacabc7f294c9eba48e52cf5d268dcf9004260a8fcb5aa9b6d9f647c2df5199b8bc9ad6e4131a4ad7ec85fa693e
7
- data.tar.gz: 74760bd8b337feb6a2b7ca3a083079c2f6c187bdd2495c9467bc7a5b5f96bb825ac8cfda8defce871295d6e10ec4e8dbed01a6776816f83cc87eabc75517537d
6
+ metadata.gz: d2a1629644bf4dcc1a523aaef694b2601f0008361468dbde35008396bb9dfe6c225857946bdad4f6dad384f11b7c763357a50a8f27ab02fb8cde23f9ce6f7271
7
+ data.tar.gz: b5a6df5e91b49e9faa2d972ee74b4aba244f83051e076cf33dda73dc9f55deee7895f8ffc4e5e13ef4d309ae33e1d9a9ef0e710bf6b12dd35545d524ec7dca7b
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dependabot/docker/utils/helpers"
3
4
  require "dependabot/experiments"
4
5
  require "dependabot/file_fetchers"
5
6
  require "dependabot/file_fetchers/base"
@@ -9,7 +10,6 @@ module Dependabot
9
10
  class FileFetcher < Dependabot::FileFetchers::Base
10
11
  YAML_REGEXP = /^[^\.]+\.ya?ml$/i
11
12
  DOCKER_REGEXP = /dockerfile/i
12
- HELM_REGEXP = /values[\-a-zA-Z_0-9]*\.yaml/i
13
13
 
14
14
  def self.required_files_in?(filenames)
15
15
  filenames.any? { |f| f.match?(DOCKER_REGEXP) } or
@@ -22,26 +22,18 @@ module Dependabot
22
22
 
23
23
  private
24
24
 
25
- def kubernetes_enabled?
26
- Experiments.enabled?(:kubernetes_updates)
27
- end
28
-
29
25
  def fetch_files
30
26
  fetched_files = []
31
27
  fetched_files += correctly_encoded_dockerfiles
32
- fetched_files += correctly_encoded_yamlfiles if kubernetes_enabled?
28
+ fetched_files += correctly_encoded_yamlfiles
33
29
 
34
30
  return fetched_files if fetched_files.any?
35
31
 
36
- if !kubernetes_enabled? && incorrectly_encoded_dockerfiles.none?
32
+ if incorrectly_encoded_dockerfiles.none? && incorrectly_encoded_yamlfiles.none?
37
33
  raise(
38
34
  Dependabot::DependencyFileNotFound,
39
- File.join(directory, "Dockerfile")
40
- )
41
- elsif incorrectly_encoded_dockerfiles.none? && incorrectly_encoded_yamlfiles.none?
42
- raise(
43
- Dependabot::DependabotError,
44
- "Found neither Kubernetes YAML nor Dockerfiles in #{directory}"
35
+ File.join(directory, "Dockerfile"),
36
+ "No Dockerfiles nor Kubernetes YAML found in #{directory}"
45
37
  )
46
38
  elsif incorrectly_encoded_dockerfiles.none?
47
39
  raise(
@@ -86,7 +78,7 @@ module Dependabot
86
78
  def correctly_encoded_yamlfiles
87
79
  candidate_files = yamlfiles.select { |f| f.content.valid_encoding? }
88
80
  candidate_files.select do |f|
89
- if f.type == "file" && f.name.match?(HELM_REGEXP)
81
+ if f.type == "file" && Utils.likely_helm_chart?(f)
90
82
  true
91
83
  else
92
84
  # This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
@@ -101,8 +101,9 @@ module Dependabot
101
101
  def version_from_digest(registry:, image:, digest:)
102
102
  return unless digest
103
103
 
104
- repo = docker_repo_name(image, registry)
105
- client = docker_registry_client(registry)
104
+ registry_details = fetch_registry_details(registry)
105
+ repo = docker_repo_name(image, registry_details["registry"])
106
+ client = docker_registry_client(registry_details["registry"], registry_details["credentials"])
106
107
  client.tags(repo, auto_paginate: true).fetch("tags").find do |tag|
107
108
  digest == client.digest(repo, tag)
108
109
  rescue DockerRegistry2::NotFound
@@ -112,46 +113,44 @@ module Dependabot
112
113
  end
113
114
  rescue DockerRegistry2::RegistryAuthenticationException,
114
115
  RestClient::Forbidden
115
- raise if standard_registry?(registry)
116
+ raise PrivateSourceAuthenticationFailure, registry_details["registry"]
117
+ rescue RestClient::Exceptions::OpenTimeout,
118
+ RestClient::Exceptions::ReadTimeout
119
+ raise if credentials_finder.using_dockerhub?(registry_details["registry"])
116
120
 
117
- raise PrivateSourceAuthenticationFailure, registry
121
+ raise PrivateSourceTimedOut, registry_details["registry"]
118
122
  end
119
123
 
120
124
  def docker_repo_name(image, registry)
121
- return image unless standard_registry?(registry)
122
- return image unless image.split("/").count < 2
125
+ return image if image.include? "/"
126
+ return "library/#{image}" if credentials_finder.using_dockerhub?(registry)
123
127
 
124
- "library/#{image}"
128
+ image
125
129
  end
126
130
 
127
- def docker_registry_client(registry)
128
- if registry
129
- credentials = registry_credentials(registry)
130
-
131
- DockerRegistry2::Registry.new(
132
- "https://#{registry}",
133
- user: credentials&.fetch("username", nil),
134
- password: credentials&.fetch("password", nil)
135
- )
136
- else
137
- DockerRegistry2::Registry.new("https://registry.hub.docker.com")
131
+ def docker_registry_client(registry_hostname, registry_credentials)
132
+ unless credentials_finder.using_dockerhub?(registry_hostname)
133
+ return DockerRegistry2::Registry.new("https://#{registry_hostname}")
138
134
  end
135
+
136
+ DockerRegistry2::Registry.new(
137
+ "https://#{registry_hostname}",
138
+ user: registry_credentials&.fetch("username", nil),
139
+ password: registry_credentials&.fetch("password", nil),
140
+ read_timeout: 10
141
+ )
139
142
  end
140
143
 
141
- def registry_credentials(registry_url)
142
- credentials_finder.credentials_for_registry(registry_url)
144
+ def fetch_registry_details(registry)
145
+ registry ||= credentials_finder.base_registry
146
+ credentials = credentials_finder.credentials_for_registry(registry)
147
+ { "registry" => registry, "credentials" => credentials }
143
148
  end
144
149
 
145
150
  def credentials_finder
146
151
  @credentials_finder ||= Utils::CredentialsFinder.new(credentials)
147
152
  end
148
153
 
149
- def standard_registry?(registry)
150
- return true if registry.nil?
151
-
152
- registry == "registry.hub.docker.com"
153
- end
154
-
155
154
  def check_required_files
156
155
  # Just check if there are any files at all.
157
156
  return if dependency_files.any?
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dependabot/docker/utils/helpers"
3
4
  require "dependabot/file_updaters"
4
5
  require "dependabot/file_updaters/base"
5
6
  require "dependabot/errors"
@@ -152,7 +153,7 @@ module Dependabot
152
153
  end
153
154
 
154
155
  def updated_yaml_content(file)
155
- updated_content = file.name == "values.yaml" ? update_helm(file) : update_image(file)
156
+ updated_content = Utils.likely_helm_chart?(file) ? update_helm(file) : update_image(file)
156
157
 
157
158
  raise "Expected content to change!" if updated_content == file.content
158
159
 
@@ -42,10 +42,9 @@ end
42
42
  module Dependabot
43
43
  module Docker
44
44
  class UpdateChecker < Dependabot::UpdateCheckers::Base
45
- VERSION_REGEX =
46
- /v?(?<version>[0-9]+(?:(?:\.[_a-z0-9]+)|(?:-(?:kb)?[0-9]+))*)/i
47
- VERSION_WITH_SFX = /^#{VERSION_REGEX}(?<suffix>-[a-z0-9.\-]+)?$/i
48
- VERSION_WITH_PFX = /^(?<prefix>[a-z0-9.\-]+-)?#{VERSION_REGEX}$/i
45
+ VERSION_REGEX = /v?(?<version>[0-9]+(?:\.[0-9]+)*(?:_[0-9]+|\.[a-z0-9]+|-(?:kb)?[0-9]+)*)/i
46
+ VERSION_WITH_SFX = /^#{VERSION_REGEX}(?<suffix>-[a-z][a-z0-9.\-]*)?$/i
47
+ VERSION_WITH_PFX = /^(?<prefix>[a-z][a-z0-9.\-]*-)?#{VERSION_REGEX}$/i
49
48
  VERSION_WITH_PFX_AND_SFX = /^(?<prefix>[a-z\-]+-)?#{VERSION_REGEX}(?<suffix>-[a-z\-]+)?$/i
50
49
  NAME_WITH_VERSION =
51
50
  /
@@ -55,7 +54,7 @@ module Dependabot
55
54
  /x
56
55
 
57
56
  def latest_version
58
- fetch_latest_version(dependency.version)
57
+ latest_version_from(dependency.version)
59
58
  end
60
59
 
61
60
  def latest_resolvable_version
@@ -72,7 +71,7 @@ module Dependabot
72
71
  dependency.requirements.map do |req|
73
72
  updated_source = req.fetch(:source).dup
74
73
  updated_source[:digest] = updated_digest if req[:source][:digest]
75
- updated_source[:tag] = fetch_latest_version(req[:source][:tag]) if req[:source][:tag]
74
+ updated_source[:tag] = latest_version_from(req[:source][:tag]) if req[:source][:tag]
76
75
 
77
76
  req.merge(source: updated_source)
78
77
  end
@@ -118,7 +117,7 @@ module Dependabot
118
117
  # Check the precision of the potentially higher tag is the same as the
119
118
  # one it would replace. In the event that it's not the same, check the
120
119
  # digests are also unequal. Avoids 'updating' ruby-2 -> ruby-2.5.1
121
- return false if old_v.split(".").count == latest_v.split(".").count
120
+ return false if precision_of(old_v) == precision_of(latest_v)
122
121
 
123
122
  digest_of(version) == digest_of(latest_version)
124
123
  end
@@ -132,33 +131,38 @@ module Dependabot
132
131
  end
133
132
  end
134
133
 
135
- # NOTE: It's important that this *always* returns a version (even if
136
- # it's the existing one) as it is what we later check the digest of.
137
- def fetch_latest_version(version)
134
+ def latest_version_from(version)
138
135
  @versions ||= {}
139
136
  return @versions[version] if @versions.key?(version)
140
137
 
141
- @versions[version] = begin
142
- return version unless version.match?(NAME_WITH_VERSION)
143
-
144
- # Prune out any downgrade tags before checking for pre-releases
145
- # (which requires a call to the registry for each tag, so can be slow)
146
- candidate_tags = comparable_tags_from_registry(version)
147
- candidate_tags = remove_version_downgrades(candidate_tags, version)
138
+ @versions[version] = fetch_latest_version(version)
139
+ end
148
140
 
149
- unless prerelease?(version)
150
- candidate_tags =
151
- candidate_tags.
152
- reject { |tag| prerelease?(tag) }
141
+ # NOTE: It's important that this *always* returns a version (even if
142
+ # it's the existing one) as it is what we later check the digest of.
143
+ def fetch_latest_version(version)
144
+ return version unless version.match?(NAME_WITH_VERSION)
145
+
146
+ # Prune out any downgrade tags before checking for pre-releases
147
+ # (which requires a call to the registry for each tag, so can be slow)
148
+ candidate_tags = comparable_tags_from_registry(version)
149
+ candidate_tags = remove_version_downgrades(candidate_tags, version)
150
+ candidate_tags = remove_prereleases(candidate_tags, version)
151
+ candidate_tags = filter_ignored(candidate_tags)
152
+ candidate_tags = sort_tags(candidate_tags, version)
153
+
154
+ latest_tag = candidate_tags.last
155
+
156
+ if latest_tag && same_precision?(latest_tag, version)
157
+ latest_tag
158
+ else
159
+ latest_same_precision_tag = remove_precision_changes(candidate_tags, version).last
160
+
161
+ if latest_same_precision_tag && digest_of(latest_same_precision_tag) == digest_of(latest_tag)
162
+ latest_same_precision_tag
163
+ else
164
+ latest_tag || version
153
165
  end
154
-
155
- latest_tag =
156
- filter_ignored(candidate_tags).
157
- max_by do |tag|
158
- [version_class.new(numeric_version_from(tag)), tag.length]
159
- end
160
-
161
- latest_tag || version
162
166
  end
163
167
  end
164
168
 
@@ -167,28 +171,37 @@ module Dependabot
167
171
  original_suffix = suffix_of(version)
168
172
  original_format = format_of(version)
169
173
 
170
- tags_from_registry.
174
+ candidate_tags =
175
+ tags_from_registry.
171
176
  select { |tag| tag.match?(NAME_WITH_VERSION) }.
172
177
  select { |tag| prefix_of(tag) == original_prefix }.
173
- select { |tag| suffix_of(tag) == original_suffix || commit_sha_suffix?(tag) }.
174
178
  select { |tag| format_of(tag) == original_format }
179
+ return candidate_tags if original_format == :sha_suffixed
180
+
181
+ candidate_tags.select { |tag| suffix_of(tag) == original_suffix }
175
182
  end
176
183
 
177
184
  def remove_version_downgrades(candidate_tags, version)
178
185
  candidate_tags.select do |tag|
179
- version_class.new(numeric_version_from(tag)) >=
180
- version_class.new(numeric_version_from(version))
186
+ comparable_version_from(tag) >=
187
+ comparable_version_from(version)
181
188
  end
182
189
  end
183
190
 
184
- def commit_sha_suffix?(tag)
185
- # Some people suffix their versions with commit SHAs. Dependabot
186
- # can't order on those but will try to, so instead we should exclude
187
- # them (unless there's a `latest` version pushed to the registry, in
188
- # which case we'll use that to find the latest version)
189
- return false unless tag.match?(/(^|\-g?)[0-9a-f]{7,}$/)
191
+ def remove_prereleases(candidate_tags, version)
192
+ return candidate_tags if prerelease?(version)
190
193
 
191
- !tag.match?(/(^|\-)\d+$/)
194
+ candidate_tags.reject { |tag| prerelease?(tag) }
195
+ end
196
+
197
+ def remove_precision_changes(candidate_tags, version)
198
+ candidate_tags.select do |tag|
199
+ same_precision?(tag, version)
200
+ end
201
+ end
202
+
203
+ def same_precision?(tag, another_tag)
204
+ precision_of(numeric_version_from(tag)) == precision_of(numeric_version_from(another_tag))
192
205
  end
193
206
 
194
207
  def version_of_latest_tag
@@ -197,13 +210,13 @@ module Dependabot
197
210
  candidate_tag =
198
211
  tags_from_registry.
199
212
  select { |tag| canonical_version?(tag) }.
200
- sort_by { |t| version_class.new(numeric_version_from(t)) }.
213
+ sort_by { |t| comparable_version_from(t) }.
201
214
  reverse.
202
215
  find { |t| digest_of(t) == latest_digest }
203
216
 
204
217
  return unless candidate_tag
205
218
 
206
- version_class.new(numeric_version_from(candidate_tag))
219
+ comparable_version_from(candidate_tag)
207
220
  end
208
221
 
209
222
  def canonical_version?(tag)
@@ -293,6 +306,7 @@ module Dependabot
293
306
 
294
307
  return :year_month if version.match?(/^[12]\d{3}(?:[.\-]|$)/)
295
308
  return :year_month_day if version.match?(/^[12]\d{5}(?:[.\-]|$)/)
309
+ return :sha_suffixed if tag.match?(/(^|\-g?)[0-9a-f]{7,}$/)
296
310
  return :build_num if version.match?(/^\d+$/)
297
311
 
298
312
  :normal
@@ -307,7 +321,11 @@ module Dependabot
307
321
  return false unless latest_digest
308
322
  return false unless version_of_latest_tag
309
323
 
310
- version_class.new(numeric_version_from(tag)) > version_of_latest_tag
324
+ comparable_version_from(tag) > version_of_latest_tag
325
+ end
326
+
327
+ def comparable_version_from(tag)
328
+ version_class.new(numeric_version_from(tag))
311
329
  end
312
330
 
313
331
  def numeric_version_from(tag)
@@ -317,8 +335,9 @@ module Dependabot
317
335
  end
318
336
 
319
337
  def registry_hostname
320
- dependency.requirements.first[:source][:registry] ||
321
- "registry.hub.docker.com"
338
+ return dependency.requirements.first[:source][:registry] if dependency.requirements.first[:source][:registry]
339
+
340
+ credentials_finder.base_registry
322
341
  end
323
342
 
324
343
  def using_dockerhub?
@@ -350,11 +369,31 @@ module Dependabot
350
369
  )
351
370
  end
352
371
 
372
+ def sort_tags(candidate_tags, version)
373
+ candidate_tags.sort do |tag_a, tag_b|
374
+ if comparable_version_from(tag_a) > comparable_version_from(tag_b)
375
+ 1
376
+ elsif comparable_version_from(tag_a) < comparable_version_from(tag_b)
377
+ -1
378
+ elsif same_precision?(tag_a, version)
379
+ 1
380
+ elsif same_precision?(tag_b, version)
381
+ -1
382
+ else
383
+ 0
384
+ end
385
+ end
386
+ end
387
+
388
+ def precision_of(version)
389
+ version.split(/[.-]/).length
390
+ end
391
+
353
392
  def filter_ignored(candidate_tags)
354
393
  filtered =
355
394
  candidate_tags.
356
395
  reject do |tag|
357
- version = version_class.new(numeric_version_from(tag))
396
+ version = comparable_version_from(tag)
358
397
  ignore_requirements.any? { |r| r.satisfied_by?(version) }
359
398
  end
360
399
  if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(candidate_tags).any?
@@ -365,9 +404,9 @@ module Dependabot
365
404
  end
366
405
 
367
406
  def filter_lower_versions(tags)
368
- versions_array = tags.map { |tag| version_class.new(numeric_version_from(tag)) }
407
+ versions_array = tags.map { |tag| comparable_version_from(tag) }
369
408
  versions_array.
370
- select { |version| version > version_class.new(numeric_version_from(dependency.version)) }
409
+ select { |version| version > comparable_version_from(dependency.version) }
371
410
  end
372
411
  end
373
412
  end
@@ -10,6 +10,7 @@ module Dependabot
10
10
  module Utils
11
11
  class CredentialsFinder
12
12
  AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/
13
+ DEFAULT_DOCKER_HUB_REGISTRY = "registry.hub.docker.com"
13
14
 
14
15
  def initialize(credentials)
15
16
  @credentials = credentials
@@ -26,6 +27,18 @@ module Dependabot
26
27
  build_aws_credentials(registry_details)
27
28
  end
28
29
 
30
+ def base_registry
31
+ @base_registry ||= credentials.find do |cred|
32
+ cred["type"] == "docker_registry" && cred["replaces-base"] == true
33
+ end
34
+ @base_registry ||= { "registry" => DEFAULT_DOCKER_HUB_REGISTRY, "credentials" => nil }
35
+ @base_registry["registry"]
36
+ end
37
+
38
+ def using_dockerhub?(registry)
39
+ registry == DEFAULT_DOCKER_HUB_REGISTRY
40
+ end
41
+
29
42
  private
30
43
 
31
44
  attr_reader :credentials
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ module Docker
5
+ module Utils
6
+ HELM_REGEXP = /values[\-a-zA-Z_0-9]*\.ya?ml$/i
7
+
8
+ def self.likely_helm_chart?(file)
9
+ file.name.match?(HELM_REGEXP)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -13,7 +13,7 @@ module Dependabot
13
13
  def initialize(version)
14
14
  release_part, update_part = version.split("_", 2)
15
15
 
16
- @release_part = Gem::Version.new(release_part)
16
+ @release_part = Gem::Version.new(release_part.tr("-", "."))
17
17
 
18
18
  @update_part = Gem::Version.new(update_part&.start_with?(/[0-9]/) ? update_part : 0)
19
19
  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.213.0
4
+ version: 0.214.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-31 00:00:00.000000000 Z
11
+ date: 2022-12-01 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.213.0
19
+ version: 0.214.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.213.0
26
+ version: 0.214.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 3.13.0
61
+ version: 4.0.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 3.13.0
68
+ version: 4.0.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 1.37.1
117
+ version: 1.39.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 1.37.1
124
+ version: 1.39.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: rubocop-performance
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -221,6 +221,7 @@ files:
221
221
  - lib/dependabot/docker/requirement.rb
222
222
  - lib/dependabot/docker/update_checker.rb
223
223
  - lib/dependabot/docker/utils/credentials_finder.rb
224
+ - lib/dependabot/docker/utils/helpers.rb
224
225
  - lib/dependabot/docker/version.rb
225
226
  homepage: https://github.com/dependabot/dependabot-core
226
227
  licenses: