dependabot-docker 0.215.0 → 0.216.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|