dependabot-docker 0.207.0 → 0.210.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9a1d777566ba278eab2e06a3498f71e91cdd35d050265f9091656f85135ea9f
4
- data.tar.gz: d235d9cb2341ea6550a9456be349776f241bba41c3f935284181350801ff1480
3
+ metadata.gz: e4ddf4a4c6b6027ae68581a6cf6e8aab2223e15c5ab2c3206037a47f2862489d
4
+ data.tar.gz: d618678f873182d7e9551d7eab0177adc1e3d81052bba60c6f618a824773f331
5
5
  SHA512:
6
- metadata.gz: b5d989f207e3d7aa3fe89aac7c55cc4ddfa584fa391c5718b38a4c2b551f6f2c61772e4cfa0aba3645923b2bba886458f89baa7b215796034abc58bd7eafe3f9
7
- data.tar.gz: 7b751732ec705abc646bfece20f37833a0300bd2a041c585e2e4bb97f1d703b331ecbbc8f3c04afcaa0275effd51b6e3ee81d76a42227195b0b629d180d70a50
6
+ metadata.gz: 42860ccf2e85d2ddf2190d9baaa04926f04956a8e8245982c952104a8a182c00de3d44587c1efa5d680fbb2b74aeeb799fd08f8d7a6b22fc7c9275ca9614de74
7
+ data.tar.gz: 6321b1c1c824e8afb24f4c5eb4856e408e3cac41de4fb5ddd18f1471dbdaac9cd946f19f834714381354c2a904d20ebcb1f863af2c69b3d3819b71942b27882a
@@ -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?(/dockerfile/i) }
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?(/dockerfile/i) }.
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 only fetches Dockerfiles, so no need to
71
- # filter here
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
- [/dockerfile/i]
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
- updated_file(
24
- file: file,
25
- content: updated_dockerfile_content(file)
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) }
@@ -76,7 +85,11 @@ module Dependabot
76
85
 
77
86
  modified_content = file.content
78
87
 
79
- old_tags.each do |old_tag|
88
+ new_tags = new_tags(file)
89
+
90
+ old_tags.zip(new_tags).each do |old_tag, new_tag|
91
+ new_tag ||= new_tags.first
92
+
80
93
  old_declaration =
81
94
  if private_registry_url(file) then "#{private_registry_url(file)}/"
82
95
  else
@@ -90,7 +103,7 @@ module Dependabot
90
103
 
91
104
  modified_content = modified_content.
92
105
  gsub(old_declaration_regex) do |old_dec|
93
- old_dec.gsub(":#{old_tag}", ":#{new_tag(file)}")
106
+ old_dec.gsub(":#{old_tag}", ":#{new_tag}")
94
107
  end
95
108
  end
96
109
 
@@ -120,10 +133,10 @@ module Dependabot
120
133
  fetch(:source).fetch(:digest)
121
134
  end
122
135
 
123
- def new_tag(file)
136
+ def new_tags(file)
124
137
  dependency.requirements.
125
- find { |r| r[:file] == file.name }.
126
- fetch(:source)[:tag]
138
+ select { |r| r[:file] == file.name }.
139
+ map { |r| r.fetch(:source)[:tag] }
127
140
  end
128
141
 
129
142
  def old_tags(file)
@@ -138,6 +151,48 @@ module Dependabot
138
151
  find { |r| r[:file] == file.name }.
139
152
  fetch(:source)[:registry]
140
153
  end
154
+
155
+ def updated_yaml_content(file)
156
+ updated_content = update_image(file)
157
+
158
+ raise "Expected content to change!" if updated_content == file.content
159
+
160
+ updated_content
161
+ end
162
+
163
+ def update_image(file)
164
+ old_images = old_yaml_images(file)
165
+ return if old_images.empty?
166
+
167
+ modified_content = file.content
168
+
169
+ old_images.each do |old_image|
170
+ old_image_regex = /^\s+(?:-\s)?image:\s+#{old_image}(?=\s|$)/
171
+ modified_content = modified_content.gsub(old_image_regex) do |old_img|
172
+ old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
173
+ end
174
+ end
175
+ modified_content
176
+ end
177
+
178
+ def new_yaml_image(file)
179
+ elt = dependency.requirements.find { |r| r[:file] == file.name }
180
+ prefix = elt.fetch(:source)[:registry] ? "#{elt.fetch(:source)[:registry]}/" : ""
181
+ digest = elt.fetch(:source)[:digest] ? "@#{elt.fetch(:source)[:digest]}" : ""
182
+ tag = elt.fetch(:source)[:tag] ? ":#{elt.fetch(:source)[:tag]}" : ""
183
+ "#{prefix}#{dependency.name}#{tag}#{digest}"
184
+ end
185
+
186
+ def old_yaml_images(file)
187
+ dependency.
188
+ previous_requirements.
189
+ select { |r| r[:file] == file.name }.map do |r|
190
+ prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
191
+ digest = r.fetch(:source)[:digest] ? "@#{r.fetch(:source)[:digest]}" : ""
192
+ tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
193
+ "#{prefix}#{dependency.name}#{tag}#{digest}"
194
+ end
195
+ end
141
196
  end
142
197
  end
143
198
  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.207.0
4
+ version: 0.210.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 00:00:00.000000000 Z
11
+ date: 2022-08-23 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.207.0
19
+ version: 0.210.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.207.0
26
+ version: 0.210.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: debase
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: parallel_tests
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 3.11.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 3.11.1
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: rake
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +142,14 @@ dependencies:
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: 1.33.0
145
+ version: 1.35.1
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: 1.33.0
152
+ version: 1.35.1
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: ruby-debug-ide
141
155
  requirement: !ruby/object:Gem::Requirement