dependabot-docker 0.212.0 → 0.214.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/file_fetcher.rb +16 -18
- data/lib/dependabot/docker/file_parser.rb +58 -43
- data/lib/dependabot/docker/file_updater.rb +36 -7
- data/lib/dependabot/docker/requirement.rb +5 -1
- data/lib/dependabot/docker/update_checker.rb +91 -53
- data/lib/dependabot/docker/utils/credentials_finder.rb +14 -1
- data/lib/dependabot/docker/utils/helpers.rb +13 -0
- data/lib/dependabot/docker/version.rb +22 -0
- metadata +14 -55
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b7aabb905dbb5a64638f936079c477b091dd8b2a05aad7cc070c10e11977cc97
|
|
4
|
+
data.tar.gz: 4cc6bbe0291ded5c2e975f2256561044700a6a5903f1d110815de0ff5a0da281
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d2a1629644bf4dcc1a523aaef694b2601f0008361468dbde35008396bb9dfe6c225857946bdad4f6dad384f11b7c763357a50a8f27ab02fb8cde23f9ce6f7271
|
|
7
|
+
data.tar.gz: b5a6df5e91b49e9faa2d972ee74b4aba244f83051e076cf33dda73dc9f55deee7895f8ffc4e5e13ef4d309ae33e1d9a9ef0e710bf6b12dd35545d524ec7dca7b
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "dependabot/docker/utils/helpers"
|
|
4
|
+
require "dependabot/experiments"
|
|
3
5
|
require "dependabot/file_fetchers"
|
|
4
6
|
require "dependabot/file_fetchers/base"
|
|
5
7
|
|
|
6
8
|
module Dependabot
|
|
7
9
|
module Docker
|
|
8
10
|
class FileFetcher < Dependabot::FileFetchers::Base
|
|
9
|
-
YAML_REGEXP = /^[^\.]+\.ya?ml$/i
|
|
10
|
-
DOCKER_REGEXP = /dockerfile/i
|
|
11
|
+
YAML_REGEXP = /^[^\.]+\.ya?ml$/i
|
|
12
|
+
DOCKER_REGEXP = /dockerfile/i
|
|
11
13
|
|
|
12
14
|
def self.required_files_in?(filenames)
|
|
13
15
|
filenames.any? { |f| f.match?(DOCKER_REGEXP) } or
|
|
@@ -20,26 +22,18 @@ module Dependabot
|
|
|
20
22
|
|
|
21
23
|
private
|
|
22
24
|
|
|
23
|
-
def kubernetes_enabled?
|
|
24
|
-
options.key?(:kubernetes_updates) && options[:kubernetes_updates]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
25
|
def fetch_files
|
|
28
26
|
fetched_files = []
|
|
29
27
|
fetched_files += correctly_encoded_dockerfiles
|
|
30
|
-
fetched_files += correctly_encoded_yamlfiles
|
|
28
|
+
fetched_files += correctly_encoded_yamlfiles
|
|
31
29
|
|
|
32
30
|
return fetched_files if fetched_files.any?
|
|
33
31
|
|
|
34
|
-
if
|
|
32
|
+
if incorrectly_encoded_dockerfiles.none? && incorrectly_encoded_yamlfiles.none?
|
|
35
33
|
raise(
|
|
36
34
|
Dependabot::DependencyFileNotFound,
|
|
37
|
-
File.join(directory, "Dockerfile")
|
|
38
|
-
|
|
39
|
-
elsif incorrectly_encoded_dockerfiles.none? && incorrectly_encoded_yamlfiles.none?
|
|
40
|
-
raise(
|
|
41
|
-
Dependabot::DependabotError,
|
|
42
|
-
"Found neither Kubernetes YAML nor Dockerfiles in #{directory}"
|
|
35
|
+
File.join(directory, "Dockerfile"),
|
|
36
|
+
"No Dockerfiles nor Kubernetes YAML found in #{directory}"
|
|
43
37
|
)
|
|
44
38
|
elsif incorrectly_encoded_dockerfiles.none?
|
|
45
39
|
raise(
|
|
@@ -84,10 +78,14 @@ module Dependabot
|
|
|
84
78
|
def correctly_encoded_yamlfiles
|
|
85
79
|
candidate_files = yamlfiles.select { |f| f.content.valid_encoding? }
|
|
86
80
|
candidate_files.select do |f|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
81
|
+
if f.type == "file" && Utils.likely_helm_chart?(f)
|
|
82
|
+
true
|
|
83
|
+
else
|
|
84
|
+
# This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
|
|
85
|
+
# in a multi-resource file had better be a valid k8s resource
|
|
86
|
+
content = ::YAML.safe_load(f.content, aliases: true)
|
|
87
|
+
likely_kubernetes_resource?(content)
|
|
88
|
+
end
|
|
91
89
|
rescue ::Psych::Exception
|
|
92
90
|
false
|
|
93
91
|
end
|
|
@@ -15,27 +15,25 @@ module Dependabot
|
|
|
15
15
|
|
|
16
16
|
# Details of Docker regular expressions is at
|
|
17
17
|
# https://github.com/docker/distribution/blob/master/reference/regexp.go
|
|
18
|
-
DOMAIN_COMPONENT =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
NAME = /\s+AS\s+(?<name>[\w-]+)/.freeze
|
|
18
|
+
DOMAIN_COMPONENT = /(?:[[:alnum:]]|[[:alnum:]][[[:alnum:]]-]*[[:alnum:]])/
|
|
19
|
+
DOMAIN = /(?:#{DOMAIN_COMPONENT}(?:\.#{DOMAIN_COMPONENT})+)/
|
|
20
|
+
REGISTRY = /(?<registry>#{DOMAIN}(?::\d+)?)/
|
|
21
|
+
|
|
22
|
+
NAME_COMPONENT = /(?:[a-z\d]+(?:(?:[._]|__|[-]*)[a-z\d]+)*)/
|
|
23
|
+
IMAGE = %r{(?<image>#{NAME_COMPONENT}(?:/#{NAME_COMPONENT})*)}
|
|
24
|
+
|
|
25
|
+
FROM = /FROM/i
|
|
26
|
+
PLATFORM = /--platform\=(?<platform>\S+)/
|
|
27
|
+
TAG = /:(?<tag>[\w][\w.-]{0,127})/
|
|
28
|
+
DIGEST = /@(?<digest>[^\s]+)/
|
|
29
|
+
NAME = /\s+AS\s+(?<name>[\w-]+)/
|
|
31
30
|
FROM_LINE =
|
|
32
31
|
%r{^#{FROM}\s+(#{PLATFORM}\s+)?(#{REGISTRY}/)?
|
|
33
|
-
#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x
|
|
32
|
+
#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x
|
|
34
33
|
|
|
35
|
-
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com
|
|
34
|
+
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/
|
|
36
35
|
|
|
37
|
-
IMAGE_SPEC =
|
|
38
|
-
%r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x.freeze
|
|
36
|
+
IMAGE_SPEC = %r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x
|
|
39
37
|
|
|
40
38
|
def parse
|
|
41
39
|
dependency_set = DependencySet.new
|
|
@@ -103,8 +101,9 @@ module Dependabot
|
|
|
103
101
|
def version_from_digest(registry:, image:, digest:)
|
|
104
102
|
return unless digest
|
|
105
103
|
|
|
106
|
-
|
|
107
|
-
|
|
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"])
|
|
108
107
|
client.tags(repo, auto_paginate: true).fetch("tags").find do |tag|
|
|
109
108
|
digest == client.digest(repo, tag)
|
|
110
109
|
rescue DockerRegistry2::NotFound
|
|
@@ -114,46 +113,44 @@ module Dependabot
|
|
|
114
113
|
end
|
|
115
114
|
rescue DockerRegistry2::RegistryAuthenticationException,
|
|
116
115
|
RestClient::Forbidden
|
|
117
|
-
raise
|
|
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"])
|
|
118
120
|
|
|
119
|
-
raise
|
|
121
|
+
raise PrivateSourceTimedOut, registry_details["registry"]
|
|
120
122
|
end
|
|
121
123
|
|
|
122
124
|
def docker_repo_name(image, registry)
|
|
123
|
-
return image
|
|
124
|
-
return image
|
|
125
|
+
return image if image.include? "/"
|
|
126
|
+
return "library/#{image}" if credentials_finder.using_dockerhub?(registry)
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
image
|
|
127
129
|
end
|
|
128
130
|
|
|
129
|
-
def docker_registry_client(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
DockerRegistry2::Registry.new(
|
|
134
|
-
"https://#{registry}",
|
|
135
|
-
user: credentials&.fetch("username", nil),
|
|
136
|
-
password: credentials&.fetch("password", nil)
|
|
137
|
-
)
|
|
138
|
-
else
|
|
139
|
-
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}")
|
|
140
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
|
+
)
|
|
141
142
|
end
|
|
142
143
|
|
|
143
|
-
def
|
|
144
|
-
credentials_finder.
|
|
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 }
|
|
145
148
|
end
|
|
146
149
|
|
|
147
150
|
def credentials_finder
|
|
148
151
|
@credentials_finder ||= Utils::CredentialsFinder.new(credentials)
|
|
149
152
|
end
|
|
150
153
|
|
|
151
|
-
def standard_registry?(registry)
|
|
152
|
-
return true if registry.nil?
|
|
153
|
-
|
|
154
|
-
registry == "registry.hub.docker.com"
|
|
155
|
-
end
|
|
156
|
-
|
|
157
154
|
def check_required_files
|
|
158
155
|
# Just check if there are any files at all.
|
|
159
156
|
return if dependency_files.any?
|
|
@@ -214,6 +211,8 @@ module Dependabot
|
|
|
214
211
|
images =
|
|
215
212
|
if !img.nil? && img.is_a?(String) && !img.empty?
|
|
216
213
|
[img]
|
|
214
|
+
elsif !img.nil? && img.is_a?(Hash) && !img.empty?
|
|
215
|
+
parse_helm(img)
|
|
217
216
|
else
|
|
218
217
|
[]
|
|
219
218
|
end
|
|
@@ -225,6 +224,22 @@ module Dependabot
|
|
|
225
224
|
# Dependencies include both Dockerfiles and yaml, select yaml.
|
|
226
225
|
dependency_files.select { |f| f.type == "file" && f.name.match?(/^[^\.]+\.ya?ml/i) }
|
|
227
226
|
end
|
|
227
|
+
|
|
228
|
+
def parse_helm(img_hash)
|
|
229
|
+
repo = img_hash.fetch("repository", nil)
|
|
230
|
+
tag = img_hash.key?("tag") ? img_hash.fetch("tag", nil) : img_hash.fetch("version", nil)
|
|
231
|
+
registry = img_hash.fetch("registry", nil)
|
|
232
|
+
|
|
233
|
+
if !repo.nil? && !registry.nil? && !tag.nil?
|
|
234
|
+
["#{registry}/#{repo}:#{tag}"]
|
|
235
|
+
elsif !repo.nil? && !tag.nil?
|
|
236
|
+
["#{repo}:#{tag}"]
|
|
237
|
+
elsif !repo.nil?
|
|
238
|
+
[repo]
|
|
239
|
+
else
|
|
240
|
+
[]
|
|
241
|
+
end
|
|
242
|
+
end
|
|
228
243
|
end
|
|
229
244
|
end
|
|
230
245
|
end
|
|
@@ -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"
|
|
@@ -7,7 +8,7 @@ require "dependabot/errors"
|
|
|
7
8
|
module Dependabot
|
|
8
9
|
module Docker
|
|
9
10
|
class FileUpdater < Dependabot::FileUpdaters::Base
|
|
10
|
-
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i
|
|
11
|
+
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i
|
|
11
12
|
|
|
12
13
|
def self.updated_files_regex
|
|
13
14
|
[
|
|
@@ -18,7 +19,6 @@ module Dependabot
|
|
|
18
19
|
|
|
19
20
|
def updated_dependency_files
|
|
20
21
|
updated_files = []
|
|
21
|
-
|
|
22
22
|
dependency_files.each do |file|
|
|
23
23
|
next unless requirement_changed?(file, dependency)
|
|
24
24
|
|
|
@@ -153,13 +153,29 @@ module Dependabot
|
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
def updated_yaml_content(file)
|
|
156
|
-
updated_content = update_image(file)
|
|
156
|
+
updated_content = Utils.likely_helm_chart?(file) ? update_helm(file) : update_image(file)
|
|
157
157
|
|
|
158
158
|
raise "Expected content to change!" if updated_content == file.content
|
|
159
159
|
|
|
160
160
|
updated_content
|
|
161
161
|
end
|
|
162
162
|
|
|
163
|
+
def update_helm(file)
|
|
164
|
+
# TODO: this won't work if two images have the same tag version
|
|
165
|
+
old_tags = old_helm_tags(file)
|
|
166
|
+
return if old_tags.empty?
|
|
167
|
+
|
|
168
|
+
modified_content = file.content
|
|
169
|
+
|
|
170
|
+
old_tags.each do |old_tag|
|
|
171
|
+
old_tag_regex = /^\s+(?:-\s)?(?:tag|version):\s+#{old_tag}(?=\s|$)/
|
|
172
|
+
modified_content = modified_content.gsub(old_tag_regex) do |old_img_tag|
|
|
173
|
+
old_img_tag.gsub(old_tag.to_s, new_yaml_tag(file).to_s)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
modified_content
|
|
177
|
+
end
|
|
178
|
+
|
|
163
179
|
def update_image(file)
|
|
164
180
|
old_images = old_yaml_images(file)
|
|
165
181
|
return if old_images.empty?
|
|
@@ -176,13 +192,18 @@ module Dependabot
|
|
|
176
192
|
end
|
|
177
193
|
|
|
178
194
|
def new_yaml_image(file)
|
|
179
|
-
|
|
180
|
-
prefix =
|
|
181
|
-
digest =
|
|
182
|
-
tag =
|
|
195
|
+
element = dependency.requirements.find { |r| r[:file] == file.name }
|
|
196
|
+
prefix = element.fetch(:source)[:registry] ? "#{element.fetch(:source)[:registry]}/" : ""
|
|
197
|
+
digest = element.fetch(:source)[:digest] ? "@#{element.fetch(:source)[:digest]}" : ""
|
|
198
|
+
tag = element.fetch(:source)[:tag] ? ":#{element.fetch(:source)[:tag]}" : ""
|
|
183
199
|
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
|
184
200
|
end
|
|
185
201
|
|
|
202
|
+
def new_yaml_tag(file)
|
|
203
|
+
element = dependency.requirements.find { |r| r[:file] == file.name }
|
|
204
|
+
element.fetch(:source)[:tag] || ""
|
|
205
|
+
end
|
|
206
|
+
|
|
186
207
|
def old_yaml_images(file)
|
|
187
208
|
dependency.
|
|
188
209
|
previous_requirements.
|
|
@@ -193,6 +214,14 @@ module Dependabot
|
|
|
193
214
|
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
|
194
215
|
end
|
|
195
216
|
end
|
|
217
|
+
|
|
218
|
+
def old_helm_tags(file)
|
|
219
|
+
dependency.
|
|
220
|
+
previous_requirements.
|
|
221
|
+
select { |r| r[:file] == file.name }.map do |r|
|
|
222
|
+
r.fetch(:source)[:tag] || ""
|
|
223
|
+
end
|
|
224
|
+
end
|
|
196
225
|
end
|
|
197
226
|
end
|
|
198
227
|
end
|
|
@@ -6,13 +6,17 @@ module Dependabot
|
|
|
6
6
|
module Docker
|
|
7
7
|
# Lifted from the bundler package manager
|
|
8
8
|
class Requirement < Gem::Requirement
|
|
9
|
-
# For consistency with other
|
|
9
|
+
# For consistency with other languages, we define a requirements array.
|
|
10
10
|
# Ruby doesn't have an `OR` separator for requirements, so it always
|
|
11
11
|
# contains a single element.
|
|
12
12
|
def self.requirements_array(requirement_string)
|
|
13
13
|
[new(requirement_string)]
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
def satisfied_by?(version)
|
|
17
|
+
super(version.release_part)
|
|
18
|
+
end
|
|
19
|
+
|
|
16
20
|
# Patches Gem::Requirement to make it accept requirement strings like
|
|
17
21
|
# "~> 4.2.5, >= 4.2.5.1" without first needing to split them.
|
|
18
22
|
def initialize(*requirements)
|
|
@@ -42,22 +42,19 @@ end
|
|
|
42
42
|
module Dependabot
|
|
43
43
|
module Docker
|
|
44
44
|
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
45
|
-
VERSION_REGEX =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
VERSION_WITH_PFX_AND_SFX =
|
|
50
|
-
/^(?<prefix>[a-z\-]+-)?#{VERSION_REGEX}(?<suffix>-[a-z\-]+)?$/i.
|
|
51
|
-
freeze
|
|
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
|
|
48
|
+
VERSION_WITH_PFX_AND_SFX = /^(?<prefix>[a-z\-]+-)?#{VERSION_REGEX}(?<suffix>-[a-z\-]+)?$/i
|
|
52
49
|
NAME_WITH_VERSION =
|
|
53
50
|
/
|
|
54
51
|
#{VERSION_WITH_PFX}|
|
|
55
52
|
#{VERSION_WITH_SFX}|
|
|
56
53
|
#{VERSION_WITH_PFX_AND_SFX}
|
|
57
|
-
/x
|
|
54
|
+
/x
|
|
58
55
|
|
|
59
56
|
def latest_version
|
|
60
|
-
|
|
57
|
+
latest_version_from(dependency.version)
|
|
61
58
|
end
|
|
62
59
|
|
|
63
60
|
def latest_resolvable_version
|
|
@@ -74,7 +71,7 @@ module Dependabot
|
|
|
74
71
|
dependency.requirements.map do |req|
|
|
75
72
|
updated_source = req.fetch(:source).dup
|
|
76
73
|
updated_source[:digest] = updated_digest if req[:source][:digest]
|
|
77
|
-
updated_source[:tag] =
|
|
74
|
+
updated_source[:tag] = latest_version_from(req[:source][:tag]) if req[:source][:tag]
|
|
78
75
|
|
|
79
76
|
req.merge(source: updated_source)
|
|
80
77
|
end
|
|
@@ -120,7 +117,7 @@ module Dependabot
|
|
|
120
117
|
# Check the precision of the potentially higher tag is the same as the
|
|
121
118
|
# one it would replace. In the event that it's not the same, check the
|
|
122
119
|
# digests are also unequal. Avoids 'updating' ruby-2 -> ruby-2.5.1
|
|
123
|
-
return false if old_v
|
|
120
|
+
return false if precision_of(old_v) == precision_of(latest_v)
|
|
124
121
|
|
|
125
122
|
digest_of(version) == digest_of(latest_version)
|
|
126
123
|
end
|
|
@@ -134,33 +131,38 @@ module Dependabot
|
|
|
134
131
|
end
|
|
135
132
|
end
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
# it's the existing one) as it is what we later check the digest of.
|
|
139
|
-
def fetch_latest_version(version)
|
|
134
|
+
def latest_version_from(version)
|
|
140
135
|
@versions ||= {}
|
|
141
136
|
return @versions[version] if @versions.key?(version)
|
|
142
137
|
|
|
143
|
-
@versions[version] =
|
|
144
|
-
|
|
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)
|
|
138
|
+
@versions[version] = fetch_latest_version(version)
|
|
139
|
+
end
|
|
150
140
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
155
165
|
end
|
|
156
|
-
|
|
157
|
-
latest_tag =
|
|
158
|
-
filter_ignored(candidate_tags).
|
|
159
|
-
max_by do |tag|
|
|
160
|
-
[version_class.new(numeric_version_from(tag)), tag.length]
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
latest_tag || version
|
|
164
166
|
end
|
|
165
167
|
end
|
|
166
168
|
|
|
@@ -169,28 +171,37 @@ module Dependabot
|
|
|
169
171
|
original_suffix = suffix_of(version)
|
|
170
172
|
original_format = format_of(version)
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
candidate_tags =
|
|
175
|
+
tags_from_registry.
|
|
173
176
|
select { |tag| tag.match?(NAME_WITH_VERSION) }.
|
|
174
177
|
select { |tag| prefix_of(tag) == original_prefix }.
|
|
175
|
-
select { |tag| suffix_of(tag) == original_suffix || commit_sha_suffix?(tag) }.
|
|
176
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 }
|
|
177
182
|
end
|
|
178
183
|
|
|
179
184
|
def remove_version_downgrades(candidate_tags, version)
|
|
180
185
|
candidate_tags.select do |tag|
|
|
181
|
-
|
|
182
|
-
|
|
186
|
+
comparable_version_from(tag) >=
|
|
187
|
+
comparable_version_from(version)
|
|
183
188
|
end
|
|
184
189
|
end
|
|
185
190
|
|
|
186
|
-
def
|
|
187
|
-
|
|
188
|
-
# can't order on those but will try to, so instead we should exclude
|
|
189
|
-
# them (unless there's a `latest` version pushed to the registry, in
|
|
190
|
-
# which case we'll use that to find the latest version)
|
|
191
|
-
return false unless tag.match?(/(^|\-g?)[0-9a-f]{7,}$/)
|
|
191
|
+
def remove_prereleases(candidate_tags, version)
|
|
192
|
+
return candidate_tags if prerelease?(version)
|
|
192
193
|
|
|
193
|
-
|
|
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))
|
|
194
205
|
end
|
|
195
206
|
|
|
196
207
|
def version_of_latest_tag
|
|
@@ -199,13 +210,13 @@ module Dependabot
|
|
|
199
210
|
candidate_tag =
|
|
200
211
|
tags_from_registry.
|
|
201
212
|
select { |tag| canonical_version?(tag) }.
|
|
202
|
-
sort_by { |t|
|
|
213
|
+
sort_by { |t| comparable_version_from(t) }.
|
|
203
214
|
reverse.
|
|
204
215
|
find { |t| digest_of(t) == latest_digest }
|
|
205
216
|
|
|
206
217
|
return unless candidate_tag
|
|
207
218
|
|
|
208
|
-
|
|
219
|
+
comparable_version_from(candidate_tag)
|
|
209
220
|
end
|
|
210
221
|
|
|
211
222
|
def canonical_version?(tag)
|
|
@@ -295,6 +306,7 @@ module Dependabot
|
|
|
295
306
|
|
|
296
307
|
return :year_month if version.match?(/^[12]\d{3}(?:[.\-]|$)/)
|
|
297
308
|
return :year_month_day if version.match?(/^[12]\d{5}(?:[.\-]|$)/)
|
|
309
|
+
return :sha_suffixed if tag.match?(/(^|\-g?)[0-9a-f]{7,}$/)
|
|
298
310
|
return :build_num if version.match?(/^\d+$/)
|
|
299
311
|
|
|
300
312
|
:normal
|
|
@@ -309,7 +321,11 @@ module Dependabot
|
|
|
309
321
|
return false unless latest_digest
|
|
310
322
|
return false unless version_of_latest_tag
|
|
311
323
|
|
|
312
|
-
|
|
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))
|
|
313
329
|
end
|
|
314
330
|
|
|
315
331
|
def numeric_version_from(tag)
|
|
@@ -319,8 +335,9 @@ module Dependabot
|
|
|
319
335
|
end
|
|
320
336
|
|
|
321
337
|
def registry_hostname
|
|
322
|
-
dependency.requirements.first[:source][:registry]
|
|
323
|
-
|
|
338
|
+
return dependency.requirements.first[:source][:registry] if dependency.requirements.first[:source][:registry]
|
|
339
|
+
|
|
340
|
+
credentials_finder.base_registry
|
|
324
341
|
end
|
|
325
342
|
|
|
326
343
|
def using_dockerhub?
|
|
@@ -347,15 +364,36 @@ module Dependabot
|
|
|
347
364
|
DockerRegistry2::Registry.new(
|
|
348
365
|
"https://#{registry_hostname}",
|
|
349
366
|
user: registry_credentials&.fetch("username", nil),
|
|
350
|
-
password: registry_credentials&.fetch("password", nil)
|
|
367
|
+
password: registry_credentials&.fetch("password", nil),
|
|
368
|
+
read_timeout: 10
|
|
351
369
|
)
|
|
352
370
|
end
|
|
353
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
|
+
|
|
354
392
|
def filter_ignored(candidate_tags)
|
|
355
393
|
filtered =
|
|
356
394
|
candidate_tags.
|
|
357
395
|
reject do |tag|
|
|
358
|
-
version =
|
|
396
|
+
version = comparable_version_from(tag)
|
|
359
397
|
ignore_requirements.any? { |r| r.satisfied_by?(version) }
|
|
360
398
|
end
|
|
361
399
|
if @raise_on_ignored && filter_lower_versions(filtered).empty? && filter_lower_versions(candidate_tags).any?
|
|
@@ -366,9 +404,9 @@ module Dependabot
|
|
|
366
404
|
end
|
|
367
405
|
|
|
368
406
|
def filter_lower_versions(tags)
|
|
369
|
-
versions_array = tags.map { |tag|
|
|
407
|
+
versions_array = tags.map { |tag| comparable_version_from(tag) }
|
|
370
408
|
versions_array.
|
|
371
|
-
select { |version| version >
|
|
409
|
+
select { |version| version > comparable_version_from(dependency.version) }
|
|
372
410
|
end
|
|
373
411
|
end
|
|
374
412
|
end
|
|
@@ -9,7 +9,8 @@ module Dependabot
|
|
|
9
9
|
module Docker
|
|
10
10
|
module Utils
|
|
11
11
|
class CredentialsFinder
|
|
12
|
-
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com
|
|
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
|
|
@@ -4,7 +4,29 @@ require "dependabot/utils"
|
|
|
4
4
|
|
|
5
5
|
module Dependabot
|
|
6
6
|
module Docker
|
|
7
|
+
# In the special case of Java, the version string may also contain
|
|
8
|
+
# optional "update number" and "identifier" components.
|
|
9
|
+
# See https://www.oracle.com/java/technologies/javase/versioning-naming.html
|
|
10
|
+
# for a description of Java versions.
|
|
11
|
+
#
|
|
7
12
|
class Version < Gem::Version
|
|
13
|
+
def initialize(version)
|
|
14
|
+
release_part, update_part = version.split("_", 2)
|
|
15
|
+
|
|
16
|
+
@release_part = Gem::Version.new(release_part.tr("-", "."))
|
|
17
|
+
|
|
18
|
+
@update_part = Gem::Version.new(update_part&.start_with?(/[0-9]/) ? update_part : 0)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :release_part
|
|
22
|
+
|
|
23
|
+
def <=>(other)
|
|
24
|
+
sort_criteria <=> other.sort_criteria
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def sort_criteria
|
|
28
|
+
[@release_part, @update_part]
|
|
29
|
+
end
|
|
8
30
|
end
|
|
9
31
|
end
|
|
10
32
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-docker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.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-
|
|
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,42 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - '='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 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.
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: debase
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - '='
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: 0.2.3
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - '='
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: 0.2.3
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: debase-ruby_core_source
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - '='
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.10.16
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - '='
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.10.16
|
|
26
|
+
version: 0.214.0
|
|
55
27
|
- !ruby/object:Gem::Dependency
|
|
56
28
|
name: debug
|
|
57
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,14 +58,14 @@ dependencies:
|
|
|
86
58
|
requirements:
|
|
87
59
|
- - "~>"
|
|
88
60
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
61
|
+
version: 4.0.0
|
|
90
62
|
type: :development
|
|
91
63
|
prerelease: false
|
|
92
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
65
|
requirements:
|
|
94
66
|
- - "~>"
|
|
95
67
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
68
|
+
version: 4.0.0
|
|
97
69
|
- !ruby/object:Gem::Dependency
|
|
98
70
|
name: rake
|
|
99
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -142,42 +114,28 @@ dependencies:
|
|
|
142
114
|
requirements:
|
|
143
115
|
- - "~>"
|
|
144
116
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: 1.
|
|
117
|
+
version: 1.39.0
|
|
146
118
|
type: :development
|
|
147
119
|
prerelease: false
|
|
148
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
121
|
requirements:
|
|
150
122
|
- - "~>"
|
|
151
123
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: 1.
|
|
124
|
+
version: 1.39.0
|
|
153
125
|
- !ruby/object:Gem::Dependency
|
|
154
126
|
name: rubocop-performance
|
|
155
127
|
requirement: !ruby/object:Gem::Requirement
|
|
156
128
|
requirements:
|
|
157
129
|
- - "~>"
|
|
158
130
|
- !ruby/object:Gem::Version
|
|
159
|
-
version: 1.
|
|
160
|
-
type: :development
|
|
161
|
-
prerelease: false
|
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
-
requirements:
|
|
164
|
-
- - "~>"
|
|
165
|
-
- !ruby/object:Gem::Version
|
|
166
|
-
version: 1.14.2
|
|
167
|
-
- !ruby/object:Gem::Dependency
|
|
168
|
-
name: ruby-debug-ide
|
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
|
170
|
-
requirements:
|
|
171
|
-
- - "~>"
|
|
172
|
-
- !ruby/object:Gem::Version
|
|
173
|
-
version: 0.7.3
|
|
131
|
+
version: 1.15.0
|
|
174
132
|
type: :development
|
|
175
133
|
prerelease: false
|
|
176
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
177
135
|
requirements:
|
|
178
136
|
- - "~>"
|
|
179
137
|
- !ruby/object:Gem::Version
|
|
180
|
-
version:
|
|
138
|
+
version: 1.15.0
|
|
181
139
|
- !ruby/object:Gem::Dependency
|
|
182
140
|
name: simplecov
|
|
183
141
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -263,6 +221,7 @@ files:
|
|
|
263
221
|
- lib/dependabot/docker/requirement.rb
|
|
264
222
|
- lib/dependabot/docker/update_checker.rb
|
|
265
223
|
- lib/dependabot/docker/utils/credentials_finder.rb
|
|
224
|
+
- lib/dependabot/docker/utils/helpers.rb
|
|
266
225
|
- lib/dependabot/docker/version.rb
|
|
267
226
|
homepage: https://github.com/dependabot/dependabot-core
|
|
268
227
|
licenses:
|
|
@@ -276,14 +235,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
276
235
|
requirements:
|
|
277
236
|
- - ">="
|
|
278
237
|
- !ruby/object:Gem::Version
|
|
279
|
-
version:
|
|
238
|
+
version: 3.1.0
|
|
280
239
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
281
240
|
requirements:
|
|
282
241
|
- - ">="
|
|
283
242
|
- !ruby/object:Gem::Version
|
|
284
|
-
version:
|
|
243
|
+
version: 3.1.0
|
|
285
244
|
requirements: []
|
|
286
|
-
rubygems_version: 3.
|
|
245
|
+
rubygems_version: 3.3.7
|
|
287
246
|
signing_key:
|
|
288
247
|
specification_version: 4
|
|
289
248
|
summary: Docker support for dependabot-common
|