dependabot-docker 0.215.0 → 0.216.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_parser.rb +4 -64
- data/lib/dependabot/docker/file_updater.rb +81 -88
- data/lib/dependabot/docker/metadata_finder.rb +15 -1
- data/lib/dependabot/docker/tag.rb +89 -0
- data/lib/dependabot/docker/update_checker.rb +133 -172
- data/lib/dependabot/docker/utils/credentials_finder.rb +17 -13
- data/lib/dependabot/docker/version.rb +19 -3
- metadata +36 -32
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f74b3c5969e731048741f4c9edd3d7d9043e029c6891473b9a22dbd03951c684
|
|
4
|
+
data.tar.gz: e180d84721d5c195361da30f1828bfd67d9b67075496ab220b6fb68f18b161f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f3dd0e3877551516396ff747ef88bf6410fa84d470c985f6be7d3efaebb3e0cef8cd053bcbfd52f4be40e993be8078ef17076b53694b5360d26f15ed78ae27b
|
|
7
|
+
data.tar.gz: '0963bc6ed481191e012eeb029988e52275c8cbc79cf3c8008494cad0863fd9981915919c9e3cce8833ef533c2a610434548ed3b511a3331be433a086e7799495'
|
|
@@ -6,7 +6,6 @@ require "dependabot/dependency"
|
|
|
6
6
|
require "dependabot/file_parsers"
|
|
7
7
|
require "dependabot/file_parsers/base"
|
|
8
8
|
require "dependabot/errors"
|
|
9
|
-
require "dependabot/docker/utils/credentials_finder"
|
|
10
9
|
|
|
11
10
|
module Dependabot
|
|
12
11
|
module Docker
|
|
@@ -25,15 +24,15 @@ module Dependabot
|
|
|
25
24
|
FROM = /FROM/i
|
|
26
25
|
PLATFORM = /--platform\=(?<platform>\S+)/
|
|
27
26
|
TAG = /:(?<tag>[\w][\w.-]{0,127})/
|
|
28
|
-
DIGEST =
|
|
27
|
+
DIGEST = /(?<digest>[0-9a-f]{64})/
|
|
29
28
|
NAME = /\s+AS\s+(?<name>[\w-]+)/
|
|
30
29
|
FROM_LINE =
|
|
31
30
|
%r{^#{FROM}\s+(#{PLATFORM}\s+)?(#{REGISTRY}/)?
|
|
32
|
-
#{IMAGE}#{TAG}
|
|
31
|
+
#{IMAGE}#{TAG}?(?:@sha256:#{DIGEST})?#{NAME}?}x
|
|
33
32
|
|
|
34
33
|
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/
|
|
35
34
|
|
|
36
|
-
IMAGE_SPEC = %r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}
|
|
35
|
+
IMAGE_SPEC = %r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?(?:@sha256:#{DIGEST})?#{NAME}?}x
|
|
37
36
|
|
|
38
37
|
def parse
|
|
39
38
|
dependency_set = DependencySet.new
|
|
@@ -77,13 +76,7 @@ module Dependabot
|
|
|
77
76
|
end
|
|
78
77
|
|
|
79
78
|
def version_from(parsed_from_line)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
version_from_digest(
|
|
83
|
-
registry: parsed_from_line.fetch("registry"),
|
|
84
|
-
image: parsed_from_line.fetch("image"),
|
|
85
|
-
digest: parsed_from_line.fetch("digest")
|
|
86
|
-
)
|
|
79
|
+
parsed_from_line.fetch("tag") || parsed_from_line.fetch("digest")
|
|
87
80
|
end
|
|
88
81
|
|
|
89
82
|
def source_from(parsed_from_line)
|
|
@@ -98,59 +91,6 @@ module Dependabot
|
|
|
98
91
|
source
|
|
99
92
|
end
|
|
100
93
|
|
|
101
|
-
def version_from_digest(registry:, image:, digest:)
|
|
102
|
-
return unless digest
|
|
103
|
-
|
|
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"])
|
|
107
|
-
client.tags(repo, auto_paginate: true).fetch("tags").find do |tag|
|
|
108
|
-
digest == client.digest(repo, tag)
|
|
109
|
-
rescue DockerRegistry2::NotFound
|
|
110
|
-
# Shouldn't happen, but it does. Example of existing tag with
|
|
111
|
-
# no manifest is "library/python", "2-windowsservercore".
|
|
112
|
-
false
|
|
113
|
-
end
|
|
114
|
-
rescue DockerRegistry2::RegistryAuthenticationException,
|
|
115
|
-
RestClient::Forbidden
|
|
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"])
|
|
120
|
-
|
|
121
|
-
raise PrivateSourceTimedOut, registry_details["registry"]
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def docker_repo_name(image, registry)
|
|
125
|
-
return image if image.include? "/"
|
|
126
|
-
return "library/#{image}" if credentials_finder.using_dockerhub?(registry)
|
|
127
|
-
|
|
128
|
-
image
|
|
129
|
-
end
|
|
130
|
-
|
|
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}")
|
|
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
|
-
)
|
|
142
|
-
end
|
|
143
|
-
|
|
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 }
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def credentials_finder
|
|
151
|
-
@credentials_finder ||= Utils::CredentialsFinder.new(credentials)
|
|
152
|
-
end
|
|
153
|
-
|
|
154
94
|
def check_required_files
|
|
155
95
|
# Just check if there are any files at all.
|
|
156
96
|
return if dependency_files.any?
|
|
@@ -56,119 +56,106 @@ module Dependabot
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def updated_dockerfile_content(file)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
old_sources = previous_sources(file)
|
|
60
|
+
new_sources = sources(file)
|
|
61
|
+
|
|
62
|
+
updated_content = file.content
|
|
63
|
+
|
|
64
|
+
old_sources.zip(new_sources).each do |old_source, new_source|
|
|
65
|
+
updated_content =
|
|
66
|
+
if specified_with_digest?(old_source)
|
|
67
|
+
update_digest_and_tag(updated_content, old_source, new_source)
|
|
68
|
+
else
|
|
69
|
+
update_tag(updated_content, old_source, new_source)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
65
72
|
|
|
66
73
|
raise "Expected content to change!" if updated_content == file.content
|
|
67
74
|
|
|
68
75
|
updated_content
|
|
69
76
|
end
|
|
70
77
|
|
|
71
|
-
def update_digest_and_tag(
|
|
72
|
-
|
|
78
|
+
def update_digest_and_tag(previous_content, old_source, new_source)
|
|
79
|
+
old_digest = old_source[:digest]
|
|
80
|
+
new_digest = new_source[:digest]
|
|
81
|
+
|
|
82
|
+
old_tag = old_source[:tag]
|
|
83
|
+
new_tag = new_source[:tag]
|
|
84
|
+
|
|
85
|
+
old_declaration_regex = /^#{FROM_REGEX}\s+.*@sha256:#{old_digest}/
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
previous_content.gsub(old_declaration_regex) do |old_dec|
|
|
75
88
|
old_dec.
|
|
76
|
-
gsub("
|
|
77
|
-
gsub(":#{
|
|
78
|
-
":#{dependency.version}")
|
|
89
|
+
gsub("@sha256:#{old_digest}", "@sha256:#{new_digest}").
|
|
90
|
+
gsub(":#{old_tag}", ":#{new_tag}")
|
|
79
91
|
end
|
|
80
92
|
end
|
|
81
93
|
|
|
82
|
-
def update_tag(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
modified_content = file.content
|
|
94
|
+
def update_tag(previous_content, old_source, new_source)
|
|
95
|
+
old_tag = old_source[:tag]
|
|
96
|
+
new_tag = new_source[:tag]
|
|
87
97
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
old_declaration =
|
|
94
|
-
if private_registry_url(file) then "#{private_registry_url(file)}/"
|
|
95
|
-
else
|
|
96
|
-
""
|
|
97
|
-
end
|
|
98
|
-
old_declaration += "#{dependency.name}:#{old_tag}"
|
|
99
|
-
escaped_declaration = Regexp.escape(old_declaration)
|
|
100
|
-
|
|
101
|
-
old_declaration_regex =
|
|
102
|
-
%r{^#{FROM_REGEX}\s+(docker\.io/)?#{escaped_declaration}(?=\s|$)}
|
|
103
|
-
|
|
104
|
-
modified_content = modified_content.
|
|
105
|
-
gsub(old_declaration_regex) do |old_dec|
|
|
106
|
-
old_dec.gsub(":#{old_tag}", ":#{new_tag}")
|
|
98
|
+
old_declaration =
|
|
99
|
+
if private_registry_url(old_source) then "#{private_registry_url(old_source)}/"
|
|
100
|
+
else
|
|
101
|
+
""
|
|
107
102
|
end
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
modified_content
|
|
111
|
-
end
|
|
103
|
+
old_declaration += "#{dependency.name}:#{old_tag}"
|
|
104
|
+
escaped_declaration = Regexp.escape(old_declaration)
|
|
112
105
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
requirements.
|
|
116
|
-
find { |r| r[:file] == file.name }.
|
|
117
|
-
fetch(:source)[:digest]
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def new_digest(file)
|
|
121
|
-
return unless specified_with_digest?(file)
|
|
106
|
+
old_declaration_regex =
|
|
107
|
+
%r{^#{FROM_REGEX}\s+(docker\.io/)?#{escaped_declaration}(?=\s|$)}
|
|
122
108
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
109
|
+
previous_content.gsub(old_declaration_regex) do |old_dec|
|
|
110
|
+
old_dec.gsub(":#{old_tag}", ":#{new_tag}")
|
|
111
|
+
end
|
|
126
112
|
end
|
|
127
113
|
|
|
128
|
-
def
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
dependency.previous_requirements.
|
|
132
|
-
find { |r| r[:file] == file.name }.
|
|
133
|
-
fetch(:source).fetch(:digest)
|
|
114
|
+
def specified_with_digest?(source)
|
|
115
|
+
source[:digest]
|
|
134
116
|
end
|
|
135
117
|
|
|
136
118
|
def new_tags(file)
|
|
137
|
-
|
|
138
|
-
select { |r| r[:file] == file.name }.
|
|
119
|
+
requirements(file).
|
|
139
120
|
map { |r| r.fetch(:source)[:tag] }
|
|
140
121
|
end
|
|
141
122
|
|
|
142
123
|
def old_tags(file)
|
|
143
|
-
|
|
144
|
-
previous_requirements.
|
|
145
|
-
select { |r| r[:file] == file.name }.
|
|
124
|
+
previous_requirements(file).
|
|
146
125
|
map { |r| r.fetch(:source)[:tag] }
|
|
147
126
|
end
|
|
148
127
|
|
|
149
|
-
def private_registry_url(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
128
|
+
def private_registry_url(source)
|
|
129
|
+
source[:registry]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def sources(file)
|
|
133
|
+
requirements(file).map { |r| r.fetch(:source) }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def previous_sources(file)
|
|
137
|
+
previous_requirements(file).map { |r| r.fetch(:source) }
|
|
153
138
|
end
|
|
154
139
|
|
|
155
140
|
def updated_yaml_content(file)
|
|
156
|
-
updated_content =
|
|
141
|
+
updated_content = file.content
|
|
142
|
+
updated_content = update_helm(file, updated_content) if Utils.likely_helm_chart?(file)
|
|
143
|
+
updated_content = update_image(file, updated_content)
|
|
157
144
|
|
|
158
145
|
raise "Expected content to change!" if updated_content == file.content
|
|
159
146
|
|
|
160
147
|
updated_content
|
|
161
148
|
end
|
|
162
149
|
|
|
163
|
-
def update_helm(file)
|
|
150
|
+
def update_helm(file, content)
|
|
164
151
|
# TODO: this won't work if two images have the same tag version
|
|
165
152
|
old_tags = old_helm_tags(file)
|
|
166
153
|
return if old_tags.empty?
|
|
167
154
|
|
|
168
|
-
modified_content =
|
|
155
|
+
modified_content = content
|
|
169
156
|
|
|
170
157
|
old_tags.each do |old_tag|
|
|
171
|
-
old_tag_regex = /^\s+(?:-\s)?(?:tag|version):\s
|
|
158
|
+
old_tag_regex = /^\s+(?:-\s)?(?:tag|version):\s+["']?#{old_tag}["']?(?=\s|$)/
|
|
172
159
|
modified_content = modified_content.gsub(old_tag_regex) do |old_img_tag|
|
|
173
160
|
old_img_tag.gsub(old_tag.to_s, new_yaml_tag(file).to_s)
|
|
174
161
|
end
|
|
@@ -176,14 +163,14 @@ module Dependabot
|
|
|
176
163
|
modified_content
|
|
177
164
|
end
|
|
178
165
|
|
|
179
|
-
def update_image(file)
|
|
166
|
+
def update_image(file, content)
|
|
180
167
|
old_images = old_yaml_images(file)
|
|
181
168
|
return if old_images.empty?
|
|
182
169
|
|
|
183
|
-
modified_content =
|
|
170
|
+
modified_content = content
|
|
184
171
|
|
|
185
172
|
old_images.each do |old_image|
|
|
186
|
-
old_image_regex = /^\s
|
|
173
|
+
old_image_regex = /^\s*(?:-\s)?image:\s+#{old_image}(?=\s|$)/
|
|
187
174
|
modified_content = modified_content.gsub(old_image_regex) do |old_img|
|
|
188
175
|
old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
|
|
189
176
|
end
|
|
@@ -194,7 +181,7 @@ module Dependabot
|
|
|
194
181
|
def new_yaml_image(file)
|
|
195
182
|
element = dependency.requirements.find { |r| r[:file] == file.name }
|
|
196
183
|
prefix = element.fetch(:source)[:registry] ? "#{element.fetch(:source)[:registry]}/" : ""
|
|
197
|
-
digest = element.fetch(:source)[:digest] ? "
|
|
184
|
+
digest = element.fetch(:source)[:digest] ? "@sha256:#{element.fetch(:source)[:digest]}" : ""
|
|
198
185
|
tag = element.fetch(:source)[:tag] ? ":#{element.fetch(:source)[:tag]}" : ""
|
|
199
186
|
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
|
200
187
|
end
|
|
@@ -205,22 +192,28 @@ module Dependabot
|
|
|
205
192
|
end
|
|
206
193
|
|
|
207
194
|
def old_yaml_images(file)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
|
215
|
-
end
|
|
195
|
+
previous_requirements(file).map do |r|
|
|
196
|
+
prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
|
|
197
|
+
digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
|
|
198
|
+
tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
|
|
199
|
+
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
|
200
|
+
end
|
|
216
201
|
end
|
|
217
202
|
|
|
218
203
|
def old_helm_tags(file)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
204
|
+
previous_requirements(file).map do |r|
|
|
205
|
+
r.fetch(:source)[:tag] || ""
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def requirements(file)
|
|
210
|
+
dependency.requirements.
|
|
211
|
+
select { |r| r[:file] == file.name }
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def previous_requirements(file)
|
|
215
|
+
dependency.previous_requirements.
|
|
216
|
+
select { |r| r[:file] == file.name }
|
|
224
217
|
end
|
|
225
218
|
end
|
|
226
219
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "dependabot/metadata_finders"
|
|
4
4
|
require "dependabot/metadata_finders/base"
|
|
5
|
+
require "dependabot/shared_helpers"
|
|
5
6
|
|
|
6
7
|
module Dependabot
|
|
7
8
|
module Docker
|
|
@@ -9,7 +10,20 @@ module Dependabot
|
|
|
9
10
|
private
|
|
10
11
|
|
|
11
12
|
def look_up_source
|
|
12
|
-
|
|
13
|
+
return if dependency.requirements.empty?
|
|
14
|
+
|
|
15
|
+
new_source = dependency.requirements.first[:source]
|
|
16
|
+
return unless new_source && new_source[:registry] && new_source[:tag]
|
|
17
|
+
|
|
18
|
+
image_ref = "#{new_source[:registry]}/#{dependency.name}:#{new_source[:tag]}"
|
|
19
|
+
image_details_output = SharedHelpers.run_shell_command("regctl image inspect #{image_ref}")
|
|
20
|
+
image_details = JSON.parse(image_details_output)
|
|
21
|
+
image_source = image_details.dig("config", "Labels", "org.opencontainers.image.source")
|
|
22
|
+
return unless image_source
|
|
23
|
+
|
|
24
|
+
Dependabot::Source.from_url(image_source)
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
Dependabot.logger.warn("Error looking up Docker source: #{e.message}")
|
|
13
27
|
nil
|
|
14
28
|
end
|
|
15
29
|
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dependabot/docker/file_parser"
|
|
4
|
+
|
|
5
|
+
module Dependabot
|
|
6
|
+
module Docker
|
|
7
|
+
class Tag
|
|
8
|
+
VERSION_REGEX = /v?(?<version>[0-9]+(?:\.[0-9]+)*(?:_[0-9]+|\.[a-z0-9]+|-(?:kb)?[0-9]+)*)/i
|
|
9
|
+
VERSION_WITH_SFX = /^#{VERSION_REGEX}(?<suffix>-[a-z][a-z0-9.\-]*)?$/i
|
|
10
|
+
VERSION_WITH_PFX = /^(?<prefix>[a-z][a-z0-9.\-]*-)?#{VERSION_REGEX}$/i
|
|
11
|
+
VERSION_WITH_PFX_AND_SFX = /^(?<prefix>[a-z\-]+-)?#{VERSION_REGEX}(?<suffix>-[a-z\-]+)?$/i
|
|
12
|
+
NAME_WITH_VERSION =
|
|
13
|
+
/
|
|
14
|
+
#{VERSION_WITH_PFX}|
|
|
15
|
+
#{VERSION_WITH_SFX}|
|
|
16
|
+
#{VERSION_WITH_PFX_AND_SFX}
|
|
17
|
+
/x
|
|
18
|
+
|
|
19
|
+
attr_reader :name
|
|
20
|
+
|
|
21
|
+
def initialize(name)
|
|
22
|
+
@name = name
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_s
|
|
26
|
+
name
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def digest?
|
|
30
|
+
name.match?(FileParser::DIGEST)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def comparable?
|
|
34
|
+
name.match?(NAME_WITH_VERSION)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def same_precision?(other)
|
|
38
|
+
other.precision == precision
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def same_but_less_precise?(other)
|
|
42
|
+
other.segments.zip(segments).all? do |segment, other_segment|
|
|
43
|
+
segment == other_segment || other_segment.nil?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def canonical?
|
|
48
|
+
return false unless numeric_version
|
|
49
|
+
return true if name == numeric_version
|
|
50
|
+
|
|
51
|
+
# .NET tags are suffixed with -sdk
|
|
52
|
+
return true if name == numeric_version + "-sdk"
|
|
53
|
+
|
|
54
|
+
name == "jdk-" + numeric_version
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def prefix
|
|
58
|
+
name.match(NAME_WITH_VERSION).named_captures.fetch("prefix")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def suffix
|
|
62
|
+
name.match(NAME_WITH_VERSION).named_captures.fetch("suffix")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def format
|
|
66
|
+
return :year_month if numeric_version.match?(/^[12]\d{3}(?:[.\-]|$)/)
|
|
67
|
+
return :year_month_day if numeric_version.match?(/^[12]\d{5}(?:[.\-]|$)/)
|
|
68
|
+
return :sha_suffixed if name.match?(/(^|\-g?)[0-9a-f]{7,}$/)
|
|
69
|
+
return :build_num if numeric_version.match?(/^\d+$/)
|
|
70
|
+
|
|
71
|
+
:normal
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def numeric_version
|
|
75
|
+
return unless comparable?
|
|
76
|
+
|
|
77
|
+
name.match(NAME_WITH_VERSION).named_captures.fetch("version").downcase
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def precision
|
|
81
|
+
segments.length
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def segments
|
|
85
|
+
numeric_version.split(/[.-]/)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -5,54 +5,15 @@ require "docker_registry2"
|
|
|
5
5
|
require "dependabot/update_checkers"
|
|
6
6
|
require "dependabot/update_checkers/base"
|
|
7
7
|
require "dependabot/errors"
|
|
8
|
+
require "dependabot/docker/tag"
|
|
9
|
+
require "dependabot/docker/file_parser"
|
|
8
10
|
require "dependabot/docker/version"
|
|
9
11
|
require "dependabot/docker/requirement"
|
|
10
12
|
require "dependabot/docker/utils/credentials_finder"
|
|
11
13
|
|
|
12
|
-
module DockerRegistry2
|
|
13
|
-
class Registry
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
# By default the Docker Registry client sets the Accept header to
|
|
17
|
-
# `application/vnd.docker.distribution.manifest.v2+json`. This is fine for
|
|
18
|
-
# most images, but for multi-architecture images, it fetches the digest of a
|
|
19
|
-
# specific architecture instead of the digest for the multi-architecture
|
|
20
|
-
# image. We override the header to tell the Docker API to vary its behavior
|
|
21
|
-
# depending on whether the image is a uses a traditional (non-list) manifest
|
|
22
|
-
# or a manifest list. If the image uses a traditional manifest, the API will
|
|
23
|
-
# return the manifest digest. If the image uses a manifest list, the API
|
|
24
|
-
# will return the manifest list digest.
|
|
25
|
-
def headers(payload: nil, bearer_token: nil)
|
|
26
|
-
headers = {}
|
|
27
|
-
headers["Authorization"] = "Bearer #{bearer_token}" unless bearer_token.nil?
|
|
28
|
-
if payload.nil?
|
|
29
|
-
headers["Accept"] = %w(
|
|
30
|
-
application/vnd.docker.distribution.manifest.v2+json
|
|
31
|
-
application/vnd.docker.distribution.manifest.list.v2+json
|
|
32
|
-
application/json
|
|
33
|
-
).join(",")
|
|
34
|
-
end
|
|
35
|
-
headers["Content-Type"] = "application/vnd.docker.distribution.manifest.v2+json" unless payload.nil?
|
|
36
|
-
|
|
37
|
-
headers
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
14
|
module Dependabot
|
|
43
15
|
module Docker
|
|
44
16
|
class UpdateChecker < Dependabot::UpdateCheckers::Base
|
|
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
|
|
49
|
-
NAME_WITH_VERSION =
|
|
50
|
-
/
|
|
51
|
-
#{VERSION_WITH_PFX}|
|
|
52
|
-
#{VERSION_WITH_SFX}|
|
|
53
|
-
#{VERSION_WITH_PFX_AND_SFX}
|
|
54
|
-
/x
|
|
55
|
-
|
|
56
17
|
def latest_version
|
|
57
18
|
latest_version_from(dependency.version)
|
|
58
19
|
end
|
|
@@ -70,8 +31,17 @@ module Dependabot
|
|
|
70
31
|
def updated_requirements
|
|
71
32
|
dependency.requirements.map do |req|
|
|
72
33
|
updated_source = req.fetch(:source).dup
|
|
73
|
-
|
|
74
|
-
|
|
34
|
+
|
|
35
|
+
tag = req[:source][:tag]
|
|
36
|
+
digest = req[:source][:digest]
|
|
37
|
+
|
|
38
|
+
if tag
|
|
39
|
+
updated_tag = latest_version_from(tag)
|
|
40
|
+
updated_source[:tag] = updated_tag
|
|
41
|
+
updated_source[:digest] = digest_of(updated_tag) if digest
|
|
42
|
+
elsif digest
|
|
43
|
+
updated_source[:digest] = digest_of("latest")
|
|
44
|
+
end
|
|
75
45
|
|
|
76
46
|
req.merge(source: updated_source)
|
|
77
47
|
end
|
|
@@ -89,148 +59,153 @@ module Dependabot
|
|
|
89
59
|
end
|
|
90
60
|
|
|
91
61
|
def version_can_update?(*)
|
|
92
|
-
|
|
62
|
+
if digest_requirements.any?
|
|
63
|
+
!digest_up_to_date?
|
|
64
|
+
else
|
|
65
|
+
!version_up_to_date?
|
|
66
|
+
end
|
|
93
67
|
end
|
|
94
68
|
|
|
95
69
|
def version_up_to_date?
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
# Otherwise, if the Dockerfile specifies a digest check that that is
|
|
103
|
-
# up-to-date
|
|
104
|
-
digest_up_to_date?
|
|
70
|
+
if digest_requirements.any?
|
|
71
|
+
version_tag_up_to_date? && digest_up_to_date?
|
|
72
|
+
else
|
|
73
|
+
version_tag_up_to_date?
|
|
74
|
+
end
|
|
105
75
|
end
|
|
106
76
|
|
|
107
|
-
def version_tag_up_to_date?
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
latest_version = fetch_latest_version(version)
|
|
77
|
+
def version_tag_up_to_date?
|
|
78
|
+
version = dependency.version
|
|
79
|
+
return unless version
|
|
111
80
|
|
|
112
|
-
|
|
113
|
-
latest_v = numeric_version_from(latest_version)
|
|
81
|
+
return true unless version_tag.comparable?
|
|
114
82
|
|
|
115
|
-
|
|
83
|
+
latest_tag = latest_tag_from(version)
|
|
116
84
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# digests are also unequal. Avoids 'updating' ruby-2 -> ruby-2.5.1
|
|
120
|
-
return false if precision_of(old_v) == precision_of(latest_v)
|
|
85
|
+
old_v = version_tag.numeric_version
|
|
86
|
+
latest_v = latest_tag.numeric_version
|
|
121
87
|
|
|
122
|
-
|
|
88
|
+
version_class.new(latest_v) <= version_class.new(old_v)
|
|
123
89
|
end
|
|
124
90
|
|
|
125
91
|
def digest_up_to_date?
|
|
126
|
-
|
|
127
|
-
next true unless
|
|
128
|
-
next true unless (new_digest = digest_of(dependency.version))
|
|
92
|
+
digest_requirements.all? do |req|
|
|
93
|
+
next true unless updated_digest
|
|
129
94
|
|
|
130
|
-
req.fetch(:source).fetch(:digest) ==
|
|
95
|
+
req.fetch(:source).fetch(:digest) == updated_digest
|
|
131
96
|
end
|
|
132
97
|
end
|
|
133
98
|
|
|
134
99
|
def latest_version_from(version)
|
|
135
|
-
|
|
136
|
-
|
|
100
|
+
latest_tag_from(version).name
|
|
101
|
+
end
|
|
137
102
|
|
|
138
|
-
|
|
103
|
+
def latest_tag_from(version)
|
|
104
|
+
@tags ||= {}
|
|
105
|
+
return @tags[version] if @tags.key?(version)
|
|
106
|
+
|
|
107
|
+
@tags[version] = fetch_latest_tag(Tag.new(version))
|
|
139
108
|
end
|
|
140
109
|
|
|
141
|
-
# NOTE: It's important that this *always* returns a
|
|
110
|
+
# NOTE: It's important that this *always* returns a tag (even if
|
|
142
111
|
# it's the existing one) as it is what we later check the digest of.
|
|
143
|
-
def
|
|
144
|
-
return
|
|
112
|
+
def fetch_latest_tag(version_tag)
|
|
113
|
+
return Tag.new(latest_digest) if version_tag.digest?
|
|
114
|
+
return version_tag unless version_tag.comparable?
|
|
145
115
|
|
|
146
116
|
# Prune out any downgrade tags before checking for pre-releases
|
|
147
117
|
# (which requires a call to the registry for each tag, so can be slow)
|
|
148
|
-
candidate_tags = comparable_tags_from_registry(
|
|
149
|
-
candidate_tags = remove_version_downgrades(candidate_tags,
|
|
150
|
-
candidate_tags = remove_prereleases(candidate_tags,
|
|
118
|
+
candidate_tags = comparable_tags_from_registry(version_tag)
|
|
119
|
+
candidate_tags = remove_version_downgrades(candidate_tags, version_tag)
|
|
120
|
+
candidate_tags = remove_prereleases(candidate_tags, version_tag)
|
|
151
121
|
candidate_tags = filter_ignored(candidate_tags)
|
|
152
|
-
candidate_tags = sort_tags(candidate_tags,
|
|
122
|
+
candidate_tags = sort_tags(candidate_tags, version_tag)
|
|
153
123
|
|
|
154
124
|
latest_tag = candidate_tags.last
|
|
125
|
+
return version_tag unless latest_tag
|
|
155
126
|
|
|
156
|
-
|
|
157
|
-
latest_tag
|
|
158
|
-
else
|
|
159
|
-
latest_same_precision_tag = remove_precision_changes(candidate_tags, version).last
|
|
127
|
+
return latest_tag if latest_tag.same_precision?(version_tag)
|
|
160
128
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
129
|
+
latest_same_precision_tag = remove_precision_changes(candidate_tags, version_tag).last
|
|
130
|
+
return latest_tag unless latest_same_precision_tag
|
|
131
|
+
|
|
132
|
+
latest_same_precision_digest = digest_of(latest_same_precision_tag.name)
|
|
133
|
+
latest_digest = digest_of(latest_tag.name)
|
|
134
|
+
|
|
135
|
+
# NOTE: Some registries don't provide digests (the API documents them as
|
|
136
|
+
# optional: https://docs.docker.com/registry/spec/api/#content-digests).
|
|
137
|
+
#
|
|
138
|
+
# In that case we can't know for sure whether the latest tag keeping
|
|
139
|
+
# existing precision is the same as the absolute latest tag.
|
|
140
|
+
#
|
|
141
|
+
# We can however, make a best-effort to avoid unwanted changes by
|
|
142
|
+
# directly looking at version numbers and checking whether the absolute
|
|
143
|
+
# latest tag is just a more precise version of the latest tag that keeps
|
|
144
|
+
# existing precision.
|
|
145
|
+
|
|
146
|
+
if latest_same_precision_digest == latest_digest && latest_same_precision_tag.same_but_less_precise?(latest_tag)
|
|
147
|
+
latest_same_precision_tag
|
|
148
|
+
else
|
|
149
|
+
latest_tag
|
|
166
150
|
end
|
|
167
151
|
end
|
|
168
152
|
|
|
169
|
-
def comparable_tags_from_registry(
|
|
170
|
-
original_prefix =
|
|
171
|
-
original_suffix =
|
|
172
|
-
original_format =
|
|
153
|
+
def comparable_tags_from_registry(original_tag)
|
|
154
|
+
original_prefix = original_tag.prefix
|
|
155
|
+
original_suffix = original_tag.suffix
|
|
156
|
+
original_format = original_tag.format
|
|
173
157
|
|
|
174
158
|
candidate_tags =
|
|
175
159
|
tags_from_registry.
|
|
176
|
-
select
|
|
177
|
-
select { |tag|
|
|
178
|
-
select { |tag|
|
|
160
|
+
select(&:comparable?).
|
|
161
|
+
select { |tag| tag.prefix == original_prefix }.
|
|
162
|
+
select { |tag| tag.format == original_format }
|
|
179
163
|
return candidate_tags if original_format == :sha_suffixed
|
|
180
164
|
|
|
181
|
-
candidate_tags.select { |tag|
|
|
165
|
+
candidate_tags.select { |tag| tag.suffix == original_suffix }
|
|
182
166
|
end
|
|
183
167
|
|
|
184
|
-
def remove_version_downgrades(candidate_tags,
|
|
168
|
+
def remove_version_downgrades(candidate_tags, version_tag)
|
|
169
|
+
current_version = comparable_version_from(version_tag)
|
|
170
|
+
|
|
185
171
|
candidate_tags.select do |tag|
|
|
186
|
-
comparable_version_from(tag) >=
|
|
187
|
-
comparable_version_from(version)
|
|
172
|
+
comparable_version_from(tag) >= current_version
|
|
188
173
|
end
|
|
189
174
|
end
|
|
190
175
|
|
|
191
|
-
def remove_prereleases(candidate_tags,
|
|
192
|
-
return candidate_tags if prerelease?(
|
|
176
|
+
def remove_prereleases(candidate_tags, version_tag)
|
|
177
|
+
return candidate_tags if prerelease?(version_tag)
|
|
193
178
|
|
|
194
179
|
candidate_tags.reject { |tag| prerelease?(tag) }
|
|
195
180
|
end
|
|
196
181
|
|
|
197
|
-
def remove_precision_changes(candidate_tags,
|
|
182
|
+
def remove_precision_changes(candidate_tags, version_tag)
|
|
198
183
|
candidate_tags.select do |tag|
|
|
199
|
-
same_precision?(
|
|
184
|
+
tag.same_precision?(version_tag)
|
|
200
185
|
end
|
|
201
186
|
end
|
|
202
187
|
|
|
203
|
-
def same_precision?(tag, another_tag)
|
|
204
|
-
precision_of(numeric_version_from(tag)) == precision_of(numeric_version_from(another_tag))
|
|
205
|
-
end
|
|
206
|
-
|
|
207
188
|
def version_of_latest_tag
|
|
208
189
|
return unless latest_digest
|
|
209
190
|
|
|
210
191
|
candidate_tag =
|
|
211
192
|
tags_from_registry.
|
|
212
|
-
select
|
|
193
|
+
select(&:canonical?).
|
|
213
194
|
sort_by { |t| comparable_version_from(t) }.
|
|
214
195
|
reverse.
|
|
215
|
-
find { |t| digest_of(t) == latest_digest }
|
|
196
|
+
find { |t| digest_of(t.name) == latest_digest }
|
|
216
197
|
|
|
217
198
|
return unless candidate_tag
|
|
218
199
|
|
|
219
200
|
comparable_version_from(candidate_tag)
|
|
220
201
|
end
|
|
221
202
|
|
|
222
|
-
def canonical_version?(tag)
|
|
223
|
-
return false unless numeric_version_from(tag)
|
|
224
|
-
return true if tag == numeric_version_from(tag)
|
|
225
|
-
|
|
226
|
-
# .NET tags are suffixed with -sdk
|
|
227
|
-
return true if tag == numeric_version_from(tag) + "-sdk"
|
|
228
|
-
|
|
229
|
-
tag == "jdk-" + numeric_version_from(tag)
|
|
230
|
-
end
|
|
231
|
-
|
|
232
203
|
def updated_digest
|
|
233
|
-
@updated_digest ||=
|
|
204
|
+
@updated_digest ||= if latest_tag_from(dependency.version).digest?
|
|
205
|
+
latest_digest
|
|
206
|
+
else
|
|
207
|
+
digest_of(latest_version)
|
|
208
|
+
end
|
|
234
209
|
end
|
|
235
210
|
|
|
236
211
|
def tags_from_registry
|
|
@@ -238,7 +213,7 @@ module Dependabot
|
|
|
238
213
|
begin
|
|
239
214
|
client = docker_registry_client
|
|
240
215
|
|
|
241
|
-
client.tags(docker_repo_name, auto_paginate: true).fetch("tags")
|
|
216
|
+
client.tags(docker_repo_name, auto_paginate: true).fetch("tags").map { |name| Tag.new(name) }
|
|
242
217
|
rescue *transient_docker_errors
|
|
243
218
|
attempt ||= 1
|
|
244
219
|
attempt += 1
|
|
@@ -257,7 +232,7 @@ module Dependabot
|
|
|
257
232
|
end
|
|
258
233
|
|
|
259
234
|
def latest_digest
|
|
260
|
-
return unless tags_from_registry.include?("latest")
|
|
235
|
+
return unless tags_from_registry.map(&:name).include?("latest")
|
|
261
236
|
|
|
262
237
|
digest_of("latest")
|
|
263
238
|
end
|
|
@@ -266,17 +241,18 @@ module Dependabot
|
|
|
266
241
|
@digests ||= {}
|
|
267
242
|
return @digests[tag] if @digests.key?(tag)
|
|
268
243
|
|
|
269
|
-
@digests[tag] =
|
|
270
|
-
|
|
271
|
-
docker_registry_client.digest(docker_repo_name, tag)
|
|
272
|
-
rescue *transient_docker_errors => e
|
|
273
|
-
attempt ||= 1
|
|
274
|
-
attempt += 1
|
|
275
|
-
return if attempt > 3 && e.is_a?(DockerRegistry2::NotFound)
|
|
276
|
-
raise if attempt > 3
|
|
244
|
+
@digests[tag] = fetch_digest_of(tag)
|
|
245
|
+
end
|
|
277
246
|
|
|
278
|
-
|
|
279
|
-
|
|
247
|
+
def fetch_digest_of(tag)
|
|
248
|
+
docker_registry_client.digest(docker_repo_name, tag)&.delete_prefix("sha256:")
|
|
249
|
+
rescue *transient_docker_errors => e
|
|
250
|
+
attempt ||= 1
|
|
251
|
+
attempt += 1
|
|
252
|
+
return if attempt > 3 && e.is_a?(DockerRegistry2::NotFound)
|
|
253
|
+
raise if attempt > 3
|
|
254
|
+
|
|
255
|
+
retry
|
|
280
256
|
rescue DockerRegistry2::RegistryAuthenticationException,
|
|
281
257
|
RestClient::Forbidden
|
|
282
258
|
raise PrivateSourceAuthenticationFailure, registry_hostname
|
|
@@ -293,31 +269,12 @@ module Dependabot
|
|
|
293
269
|
]
|
|
294
270
|
end
|
|
295
271
|
|
|
296
|
-
def prefix_of(tag)
|
|
297
|
-
tag.match(NAME_WITH_VERSION).named_captures.fetch("prefix")
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
def suffix_of(tag)
|
|
301
|
-
tag.match(NAME_WITH_VERSION).named_captures.fetch("suffix")
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
def format_of(tag)
|
|
305
|
-
version = numeric_version_from(tag)
|
|
306
|
-
|
|
307
|
-
return :year_month if version.match?(/^[12]\d{3}(?:[.\-]|$)/)
|
|
308
|
-
return :year_month_day if version.match?(/^[12]\d{5}(?:[.\-]|$)/)
|
|
309
|
-
return :sha_suffixed if tag.match?(/(^|\-g?)[0-9a-f]{7,}$/)
|
|
310
|
-
return :build_num if version.match?(/^\d+$/)
|
|
311
|
-
|
|
312
|
-
:normal
|
|
313
|
-
end
|
|
314
|
-
|
|
315
272
|
def prerelease?(tag)
|
|
316
|
-
return true if
|
|
273
|
+
return true if tag.numeric_version.gsub(/kb/i, "").match?(/[a-zA-Z]/)
|
|
317
274
|
|
|
318
275
|
# If we're dealing with a numeric version we can compare it against
|
|
319
276
|
# the digest for the `latest` tag.
|
|
320
|
-
return false unless
|
|
277
|
+
return false unless tag.numeric_version
|
|
321
278
|
return false unless latest_digest
|
|
322
279
|
return false unless version_of_latest_tag
|
|
323
280
|
|
|
@@ -325,13 +282,7 @@ module Dependabot
|
|
|
325
282
|
end
|
|
326
283
|
|
|
327
284
|
def comparable_version_from(tag)
|
|
328
|
-
version_class.new(
|
|
329
|
-
end
|
|
330
|
-
|
|
331
|
-
def numeric_version_from(tag)
|
|
332
|
-
return unless tag.match?(NAME_WITH_VERSION)
|
|
333
|
-
|
|
334
|
-
tag.match(NAME_WITH_VERSION).named_captures.fetch("version").downcase
|
|
285
|
+
version_class.new(tag.numeric_version)
|
|
335
286
|
end
|
|
336
287
|
|
|
337
288
|
def registry_hostname
|
|
@@ -365,19 +316,20 @@ module Dependabot
|
|
|
365
316
|
"https://#{registry_hostname}",
|
|
366
317
|
user: registry_credentials&.fetch("username", nil),
|
|
367
318
|
password: registry_credentials&.fetch("password", nil),
|
|
368
|
-
read_timeout: 10
|
|
319
|
+
read_timeout: 10,
|
|
320
|
+
http_options: { proxy: ENV.fetch("HTTPS_PROXY", nil) }
|
|
369
321
|
)
|
|
370
322
|
end
|
|
371
323
|
|
|
372
|
-
def sort_tags(candidate_tags,
|
|
324
|
+
def sort_tags(candidate_tags, version_tag)
|
|
373
325
|
candidate_tags.sort do |tag_a, tag_b|
|
|
374
326
|
if comparable_version_from(tag_a) > comparable_version_from(tag_b)
|
|
375
327
|
1
|
|
376
328
|
elsif comparable_version_from(tag_a) < comparable_version_from(tag_b)
|
|
377
329
|
-1
|
|
378
|
-
elsif same_precision?(
|
|
330
|
+
elsif tag_a.same_precision?(version_tag)
|
|
379
331
|
1
|
|
380
|
-
elsif same_precision?(
|
|
332
|
+
elsif tag_b.same_precision?(version_tag)
|
|
381
333
|
-1
|
|
382
334
|
else
|
|
383
335
|
0
|
|
@@ -385,10 +337,6 @@ module Dependabot
|
|
|
385
337
|
end
|
|
386
338
|
end
|
|
387
339
|
|
|
388
|
-
def precision_of(version)
|
|
389
|
-
version.split(/[.-]/).length
|
|
390
|
-
end
|
|
391
|
-
|
|
392
340
|
def filter_ignored(candidate_tags)
|
|
393
341
|
filtered =
|
|
394
342
|
candidate_tags.
|
|
@@ -396,7 +344,10 @@ module Dependabot
|
|
|
396
344
|
version = comparable_version_from(tag)
|
|
397
345
|
ignore_requirements.any? { |r| r.satisfied_by?(version) }
|
|
398
346
|
end
|
|
399
|
-
if @raise_on_ignored &&
|
|
347
|
+
if @raise_on_ignored &&
|
|
348
|
+
filter_lower_versions(filtered).empty? &&
|
|
349
|
+
filter_lower_versions(candidate_tags).any? &&
|
|
350
|
+
digest_requirements.none?
|
|
400
351
|
raise AllVersionsIgnored
|
|
401
352
|
end
|
|
402
353
|
|
|
@@ -404,9 +355,19 @@ module Dependabot
|
|
|
404
355
|
end
|
|
405
356
|
|
|
406
357
|
def filter_lower_versions(tags)
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
358
|
+
tags.select do |tag|
|
|
359
|
+
comparable_version_from(tag) > comparable_version_from(version_tag)
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def digest_requirements
|
|
364
|
+
dependency.requirements.select do |requirement|
|
|
365
|
+
requirement.dig(:source, :digest)
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def version_tag
|
|
370
|
+
@version_tag ||= Tag.new(dependency.version)
|
|
410
371
|
end
|
|
411
372
|
end
|
|
412
373
|
end
|
|
@@ -47,29 +47,33 @@ module Dependabot
|
|
|
47
47
|
# If credentials have been generated from AWS we can just return them
|
|
48
48
|
return registry_details if registry_details["username"] == "AWS"
|
|
49
49
|
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
# Otherwise, we need to use the provided Access Key ID and secret to
|
|
54
|
-
# generate a temporary username and password
|
|
50
|
+
# Build a client either with explicit creds or default creds
|
|
51
|
+
registry_hostname = registry_details.fetch("registry")
|
|
52
|
+
region = registry_hostname.match(AWS_ECR_URL).named_captures.fetch("region")
|
|
55
53
|
aws_credentials = Aws::Credentials.new(
|
|
56
54
|
registry_details["username"],
|
|
57
55
|
registry_details["password"]
|
|
58
56
|
)
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
ecr_client =
|
|
59
|
+
if aws_credentials.set?
|
|
60
|
+
Aws::ECR::Client.new(region: region, credentials: aws_credentials)
|
|
61
|
+
else
|
|
62
|
+
# Let the client check default locations for credentials
|
|
63
|
+
Aws::ECR::Client.new(region: region)
|
|
64
|
+
end
|
|
63
65
|
|
|
66
|
+
# If the client still lacks credentials, we might be running within GitHub's
|
|
67
|
+
# Dependabot Service, in which case we might get them from the proxy
|
|
68
|
+
return registry_details if ecr_client.config.credentials.nil?
|
|
69
|
+
|
|
70
|
+
# Otherwise, we need to use the provided Access Key ID and secret to
|
|
71
|
+
# generate a temporary username and password
|
|
64
72
|
@authorization_tokens ||= {}
|
|
65
73
|
@authorization_tokens[registry_hostname] ||=
|
|
66
|
-
|
|
67
|
-
get_authorization_token.authorization_data.first.
|
|
68
|
-
authorization_token
|
|
69
|
-
|
|
74
|
+
ecr_client.get_authorization_token.authorization_data.first.authorization_token
|
|
70
75
|
username, password =
|
|
71
76
|
Base64.decode64(@authorization_tokens[registry_hostname]).split(":")
|
|
72
|
-
|
|
73
77
|
registry_details.merge("username" => username, "password" => password)
|
|
74
78
|
rescue Aws::Errors::MissingCredentialsError,
|
|
75
79
|
Aws::ECR::Errors::UnrecognizedClientException,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "dependabot/version"
|
|
3
4
|
require "dependabot/utils"
|
|
4
5
|
|
|
5
6
|
module Dependabot
|
|
@@ -9,13 +10,28 @@ module Dependabot
|
|
|
9
10
|
# See https://www.oracle.com/java/technologies/javase/versioning-naming.html
|
|
10
11
|
# for a description of Java versions.
|
|
11
12
|
#
|
|
12
|
-
class Version <
|
|
13
|
+
class Version < Dependabot::Version
|
|
13
14
|
def initialize(version)
|
|
14
15
|
release_part, update_part = version.split("_", 2)
|
|
15
16
|
|
|
16
|
-
@release_part =
|
|
17
|
+
@release_part = Dependabot::Version.new(release_part.tr("-", "."))
|
|
17
18
|
|
|
18
|
-
@update_part =
|
|
19
|
+
@update_part = Dependabot::Version.new(update_part&.start_with?(/[0-9]/) ? update_part : 0)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.correct?(version)
|
|
23
|
+
super(new(version).to_semver)
|
|
24
|
+
rescue ArgumentError
|
|
25
|
+
# if we can't instantiate a version, it can't be correct
|
|
26
|
+
false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_semver
|
|
30
|
+
@release_part.to_semver
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def segments
|
|
34
|
+
@release_part.segments
|
|
19
35
|
end
|
|
20
36
|
|
|
21
37
|
attr_reader :release_part
|
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.216.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-04-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dependabot-common
|
|
@@ -16,28 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - '='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.216.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.
|
|
26
|
+
version: 0.216.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: debug
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.
|
|
33
|
+
version: 1.7.1
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.
|
|
40
|
+
version: 1.7.1
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: gpgme
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,14 +58,14 @@ dependencies:
|
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: 4.
|
|
61
|
+
version: 4.2.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: 4.
|
|
68
|
+
version: 4.2.0
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: rake
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -86,70 +86,70 @@ dependencies:
|
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '3.
|
|
89
|
+
version: '3.12'
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '3.
|
|
96
|
+
version: '3.12'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: rspec-its
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
101
|
- - "~>"
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '1.
|
|
103
|
+
version: '1.3'
|
|
104
104
|
type: :development
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
108
|
- - "~>"
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '1.
|
|
110
|
+
version: '1.3'
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
112
|
name: rubocop
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
114
114
|
requirements:
|
|
115
115
|
- - "~>"
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: 1.
|
|
117
|
+
version: 1.48.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.
|
|
124
|
+
version: 1.48.0
|
|
125
125
|
- !ruby/object:Gem::Dependency
|
|
126
126
|
name: rubocop-performance
|
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
|
128
128
|
requirements:
|
|
129
129
|
- - "~>"
|
|
130
130
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: 1.
|
|
131
|
+
version: 1.17.1
|
|
132
132
|
type: :development
|
|
133
133
|
prerelease: false
|
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
|
136
136
|
- - "~>"
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: 1.
|
|
138
|
+
version: 1.17.1
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
140
|
name: simplecov
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
|
142
142
|
requirements:
|
|
143
143
|
- - "~>"
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: 0.
|
|
145
|
+
version: 0.22.0
|
|
146
146
|
type: :development
|
|
147
147
|
prerelease: false
|
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
149
|
requirements:
|
|
150
150
|
- - "~>"
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: 0.
|
|
152
|
+
version: 0.22.0
|
|
153
153
|
- !ruby/object:Gem::Dependency
|
|
154
154
|
name: simplecov-console
|
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -182,33 +182,34 @@ dependencies:
|
|
|
182
182
|
name: vcr
|
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
|
184
184
|
requirements:
|
|
185
|
-
- -
|
|
185
|
+
- - "~>"
|
|
186
186
|
- !ruby/object:Gem::Version
|
|
187
|
-
version: 6.1
|
|
187
|
+
version: '6.1'
|
|
188
188
|
type: :development
|
|
189
189
|
prerelease: false
|
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
|
191
191
|
requirements:
|
|
192
|
-
- -
|
|
192
|
+
- - "~>"
|
|
193
193
|
- !ruby/object:Gem::Version
|
|
194
|
-
version: 6.1
|
|
194
|
+
version: '6.1'
|
|
195
195
|
- !ruby/object:Gem::Dependency
|
|
196
196
|
name: webmock
|
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
|
198
198
|
requirements:
|
|
199
199
|
- - "~>"
|
|
200
200
|
- !ruby/object:Gem::Version
|
|
201
|
-
version: '3.
|
|
201
|
+
version: '3.18'
|
|
202
202
|
type: :development
|
|
203
203
|
prerelease: false
|
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
|
205
205
|
requirements:
|
|
206
206
|
- - "~>"
|
|
207
207
|
- !ruby/object:Gem::Version
|
|
208
|
-
version: '3.
|
|
209
|
-
description:
|
|
210
|
-
|
|
211
|
-
|
|
208
|
+
version: '3.18'
|
|
209
|
+
description: Dependabot-Docker provides support for bumping Docker image tags via
|
|
210
|
+
Dependabot. If you want support for multiple package managers, you probably want
|
|
211
|
+
the meta-gem dependabot-omnibus.
|
|
212
|
+
email: opensource@github.com
|
|
212
213
|
executables: []
|
|
213
214
|
extensions: []
|
|
214
215
|
extra_rdoc_files: []
|
|
@@ -219,6 +220,7 @@ files:
|
|
|
219
220
|
- lib/dependabot/docker/file_updater.rb
|
|
220
221
|
- lib/dependabot/docker/metadata_finder.rb
|
|
221
222
|
- lib/dependabot/docker/requirement.rb
|
|
223
|
+
- lib/dependabot/docker/tag.rb
|
|
222
224
|
- lib/dependabot/docker/update_checker.rb
|
|
223
225
|
- lib/dependabot/docker/utils/credentials_finder.rb
|
|
224
226
|
- lib/dependabot/docker/utils/helpers.rb
|
|
@@ -226,7 +228,9 @@ files:
|
|
|
226
228
|
homepage: https://github.com/dependabot/dependabot-core
|
|
227
229
|
licenses:
|
|
228
230
|
- Nonstandard
|
|
229
|
-
metadata:
|
|
231
|
+
metadata:
|
|
232
|
+
issue_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
233
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/blob/main/CHANGELOG.md
|
|
230
234
|
post_install_message:
|
|
231
235
|
rdoc_options: []
|
|
232
236
|
require_paths:
|
|
@@ -242,8 +246,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
242
246
|
- !ruby/object:Gem::Version
|
|
243
247
|
version: 3.1.0
|
|
244
248
|
requirements: []
|
|
245
|
-
rubygems_version: 3.3.
|
|
249
|
+
rubygems_version: 3.3.26
|
|
246
250
|
signing_key:
|
|
247
251
|
specification_version: 4
|
|
248
|
-
summary:
|
|
252
|
+
summary: Provides Dependabot support for Docker
|
|
249
253
|
test_files: []
|