dependabot-docker 0.208.0 → 0.209.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 +51 -4
- data/lib/dependabot/docker/file_parser.rb +74 -3
- data/lib/dependabot/docker/file_updater.rb +58 -6
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 00f4377178bcfeb2dab9cd43cfc073184f3889bfc58c6775e43cb4af74d6d51a
|
|
4
|
+
data.tar.gz: 4688f5144e93f55a744a5c78b0f27b0c976147ac0f947b7898525dbdee4dc77a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '08567ef1cd81d990857e8f028ec982fdb263469a3eab619cc84cdc4deb6d7bed1d5d85135d9585f88871404279bf6be47571b7896a92c5e32952f99df0aee821'
|
|
7
|
+
data.tar.gz: 04b846444b46050a0a899f7a510a07b7be67e97e60e3fbea3d5d822940a45b9301f86bd0a37d36e4a4e97d369acd8ab92e9a25ba4263facd2b322db4dd4bf9af
|
|
@@ -6,27 +6,46 @@ require "dependabot/file_fetchers/base"
|
|
|
6
6
|
module Dependabot
|
|
7
7
|
module Docker
|
|
8
8
|
class FileFetcher < Dependabot::FileFetchers::Base
|
|
9
|
+
YAML_REGEXP = /^[^\.]+\.ya?ml$/i.freeze
|
|
10
|
+
DOCKER_REGEXP = /dockerfile/i.freeze
|
|
11
|
+
|
|
9
12
|
def self.required_files_in?(filenames)
|
|
10
|
-
filenames.any? { |f| f.match?(
|
|
13
|
+
filenames.any? { |f| f.match?(DOCKER_REGEXP) } or
|
|
14
|
+
filenames.any? { |f| f.match?(YAML_REGEXP) }
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def self.required_files_message
|
|
14
|
-
"Repo must contain a Dockerfile."
|
|
18
|
+
"Repo must contain a Dockerfile or Kubernetes YAML files."
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
private
|
|
18
22
|
|
|
23
|
+
def kubernetes_enabled?
|
|
24
|
+
options.key?(:kubernetes_updates) && options[:kubernetes_updates]
|
|
25
|
+
end
|
|
26
|
+
|
|
19
27
|
def fetch_files
|
|
20
28
|
fetched_files = []
|
|
21
29
|
fetched_files += correctly_encoded_dockerfiles
|
|
30
|
+
fetched_files += correctly_encoded_yamlfiles if kubernetes_enabled?
|
|
22
31
|
|
|
23
32
|
return fetched_files if fetched_files.any?
|
|
24
33
|
|
|
25
|
-
if incorrectly_encoded_dockerfiles.none?
|
|
34
|
+
if !kubernetes_enabled? && incorrectly_encoded_dockerfiles.none?
|
|
26
35
|
raise(
|
|
27
36
|
Dependabot::DependencyFileNotFound,
|
|
28
37
|
File.join(directory, "Dockerfile")
|
|
29
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}"
|
|
43
|
+
)
|
|
44
|
+
elsif incorrectly_encoded_dockerfiles.none?
|
|
45
|
+
raise(
|
|
46
|
+
Dependabot::DependencyFileNotParseable,
|
|
47
|
+
incorrectly_encoded_yamlfiles.first.path
|
|
48
|
+
)
|
|
30
49
|
else
|
|
31
50
|
raise(
|
|
32
51
|
Dependabot::DependencyFileNotParseable,
|
|
@@ -38,7 +57,7 @@ module Dependabot
|
|
|
38
57
|
def dockerfiles
|
|
39
58
|
@dockerfiles ||=
|
|
40
59
|
repo_contents(raise_errors: false).
|
|
41
|
-
select { |f| f.type == "file" && f.name.match?(
|
|
60
|
+
select { |f| f.type == "file" && f.name.match?(DOCKER_REGEXP) }.
|
|
42
61
|
map { |f| fetch_file_from_host(f.name) }
|
|
43
62
|
end
|
|
44
63
|
|
|
@@ -49,6 +68,34 @@ module Dependabot
|
|
|
49
68
|
def incorrectly_encoded_dockerfiles
|
|
50
69
|
dockerfiles.reject { |f| f.content.valid_encoding? }
|
|
51
70
|
end
|
|
71
|
+
|
|
72
|
+
def yamlfiles
|
|
73
|
+
@yamlfiles ||=
|
|
74
|
+
repo_contents(raise_errors: false).
|
|
75
|
+
select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }.
|
|
76
|
+
map { |f| fetch_file_from_host(f.name) }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def likely_kubernetes_resource?(resource)
|
|
80
|
+
# Heuristic for being a Kubernetes resource. We could make this tighter but this probably works well.
|
|
81
|
+
resource.is_a?(::Hash) && resource.key?("apiVersion") && resource.key?("kind")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def correctly_encoded_yamlfiles
|
|
85
|
+
candidate_files = yamlfiles.select { |f| f.content.valid_encoding? }
|
|
86
|
+
candidate_files.select do |f|
|
|
87
|
+
# This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
|
|
88
|
+
# in a multi-resource file had better be a valid k8s resource
|
|
89
|
+
content = ::YAML.safe_load(f.content, aliases: true)
|
|
90
|
+
likely_kubernetes_resource?(content)
|
|
91
|
+
rescue ::Psych::Exception
|
|
92
|
+
false
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def incorrectly_encoded_yamlfiles
|
|
97
|
+
yamlfiles.reject { |f| f.content.valid_encoding? }
|
|
98
|
+
end
|
|
52
99
|
end
|
|
53
100
|
end
|
|
54
101
|
end
|
|
@@ -34,6 +34,9 @@ module Dependabot
|
|
|
34
34
|
|
|
35
35
|
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/.freeze
|
|
36
36
|
|
|
37
|
+
IMAGE_SPEC =
|
|
38
|
+
%r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?#{DIGEST}?#{NAME}?}x.freeze
|
|
39
|
+
|
|
37
40
|
def parse
|
|
38
41
|
dependency_set = DependencySet.new
|
|
39
42
|
|
|
@@ -61,15 +64,18 @@ module Dependabot
|
|
|
61
64
|
end
|
|
62
65
|
end
|
|
63
66
|
|
|
67
|
+
manifest_files.each do |file|
|
|
68
|
+
dependency_set += workfile_file_dependencies(file)
|
|
69
|
+
end
|
|
70
|
+
|
|
64
71
|
dependency_set.dependencies
|
|
65
72
|
end
|
|
66
73
|
|
|
67
74
|
private
|
|
68
75
|
|
|
69
76
|
def dockerfiles
|
|
70
|
-
# The Docker file fetcher
|
|
71
|
-
|
|
72
|
-
dependency_files
|
|
77
|
+
# The Docker file fetcher fetches Dockerfiles and yaml files. Reject yaml files.
|
|
78
|
+
dependency_files.reject { |f| f.type == "file" && f.name.match?(/^[^\.]+\.ya?ml/i) }
|
|
73
79
|
end
|
|
74
80
|
|
|
75
81
|
def version_from(parsed_from_line)
|
|
@@ -154,6 +160,71 @@ module Dependabot
|
|
|
154
160
|
|
|
155
161
|
raise "No Dockerfile!"
|
|
156
162
|
end
|
|
163
|
+
|
|
164
|
+
def workfile_file_dependencies(file)
|
|
165
|
+
dependency_set = DependencySet.new
|
|
166
|
+
|
|
167
|
+
resources = file.content.split(/^---$/).map(&:strip).reject(&:empty?) # assuming a yaml file
|
|
168
|
+
resources.flat_map do |resource|
|
|
169
|
+
json = YAML.safe_load(resource, aliases: true)
|
|
170
|
+
images = deep_fetch_images(json).uniq
|
|
171
|
+
|
|
172
|
+
images.each do |string|
|
|
173
|
+
# TODO: Support Docker references and path references
|
|
174
|
+
details = string.match(IMAGE_SPEC).named_captures
|
|
175
|
+
details["registry"] = nil if details["registry"] == "docker.io"
|
|
176
|
+
|
|
177
|
+
version = version_from(details)
|
|
178
|
+
next unless version
|
|
179
|
+
|
|
180
|
+
dependency_set << build_image_dependency(file, details, version)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
dependency_set
|
|
185
|
+
rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
|
|
186
|
+
raise Dependabot::DependencyFileNotParseable, file.path
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def build_image_dependency(file, details, version)
|
|
190
|
+
Dependency.new(
|
|
191
|
+
name: details.fetch("image"),
|
|
192
|
+
version: version,
|
|
193
|
+
package_manager: "docker",
|
|
194
|
+
requirements: [
|
|
195
|
+
requirement: nil,
|
|
196
|
+
groups: [],
|
|
197
|
+
file: file.name,
|
|
198
|
+
source: source_from(details)
|
|
199
|
+
]
|
|
200
|
+
)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def deep_fetch_images(json_obj)
|
|
204
|
+
case json_obj
|
|
205
|
+
when Hash then deep_fetch_images_from_hash(json_obj)
|
|
206
|
+
when Array then json_obj.flat_map { |o| deep_fetch_images(o) }
|
|
207
|
+
else []
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def deep_fetch_images_from_hash(json_object)
|
|
212
|
+
img = json_object.fetch("image", nil)
|
|
213
|
+
|
|
214
|
+
images =
|
|
215
|
+
if !img.nil? && img.is_a?(String) && !img.empty?
|
|
216
|
+
[img]
|
|
217
|
+
else
|
|
218
|
+
[]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
images + json_object.values.flat_map { |obj| deep_fetch_images(obj) }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def manifest_files
|
|
225
|
+
# Dependencies include both Dockerfiles and yaml, select yaml.
|
|
226
|
+
dependency_files.select { |f| f.type == "file" && f.name.match?(/^[^\.]+\.ya?ml/i) }
|
|
227
|
+
end
|
|
157
228
|
end
|
|
158
229
|
end
|
|
159
230
|
end
|
|
@@ -10,7 +10,10 @@ module Dependabot
|
|
|
10
10
|
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i.freeze
|
|
11
11
|
|
|
12
12
|
def self.updated_files_regex
|
|
13
|
-
[
|
|
13
|
+
[
|
|
14
|
+
/dockerfile/i,
|
|
15
|
+
/^[^\.]+\.ya?ml/i
|
|
16
|
+
]
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def updated_dependency_files
|
|
@@ -19,11 +22,17 @@ module Dependabot
|
|
|
19
22
|
dependency_files.each do |file|
|
|
20
23
|
next unless requirement_changed?(file, dependency)
|
|
21
24
|
|
|
22
|
-
updated_files <<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
updated_files << if file.name.match?(/^[^\.]+\.ya?ml/i)
|
|
26
|
+
updated_file(
|
|
27
|
+
file: file,
|
|
28
|
+
content: updated_yaml_content(file)
|
|
29
|
+
)
|
|
30
|
+
else
|
|
31
|
+
updated_file(
|
|
32
|
+
file: file,
|
|
33
|
+
content: updated_dockerfile_content(file)
|
|
34
|
+
)
|
|
35
|
+
end
|
|
27
36
|
end
|
|
28
37
|
|
|
29
38
|
updated_files.reject! { |f| dependency_files.include?(f) }
|
|
@@ -138,6 +147,49 @@ module Dependabot
|
|
|
138
147
|
find { |r| r[:file] == file.name }.
|
|
139
148
|
fetch(:source)[:registry]
|
|
140
149
|
end
|
|
150
|
+
|
|
151
|
+
def updated_yaml_content(file)
|
|
152
|
+
updated_content = update_image(file)
|
|
153
|
+
|
|
154
|
+
raise "Expected content to change!" if updated_content == file.content
|
|
155
|
+
|
|
156
|
+
updated_content
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def update_image(file)
|
|
160
|
+
old_images = old_yaml_images(file)
|
|
161
|
+
return if old_images.empty?
|
|
162
|
+
|
|
163
|
+
modified_content = file.content
|
|
164
|
+
|
|
165
|
+
old_images.each do |old_image|
|
|
166
|
+
old_image_regex = /^\s+image:\s+#{old_image}(?=\s|$)/
|
|
167
|
+
modified_content = modified_content.gsub(old_image_regex) do |old_img|
|
|
168
|
+
old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
modified_content
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def new_yaml_image(file)
|
|
176
|
+
elt = dependency.requirements.find { |r| r[:file] == file.name }
|
|
177
|
+
prefix = elt.fetch(:source)[:registry] ? "#{elt.fetch(:source)[:registry]}/" : ""
|
|
178
|
+
digest = elt.fetch(:source)[:digest] ? "@#{elt.fetch(:source)[:digest]}" : ""
|
|
179
|
+
tag = elt.fetch(:source)[:tag] ? ":#{elt.fetch(:source)[:tag]}" : ""
|
|
180
|
+
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def old_yaml_images(file)
|
|
184
|
+
dependency.
|
|
185
|
+
previous_requirements.
|
|
186
|
+
select { |r| r[:file] == file.name }.map do |r|
|
|
187
|
+
prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
|
|
188
|
+
digest = r.fetch(:source)[:digest] ? "@#{r.fetch(:source)[:digest]}" : ""
|
|
189
|
+
tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
|
|
190
|
+
"#{prefix}#{dependency.name}#{tag}#{digest}"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
141
193
|
end
|
|
142
194
|
end
|
|
143
195
|
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.209.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-08-
|
|
11
|
+
date: 2022-08-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dependabot-common
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - '='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.209.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.209.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: debase
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|