dependabot-docker 0.297.0 → 0.297.1
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 +29 -72
- data/lib/dependabot/docker/file_parser.rb +37 -95
- data/lib/dependabot/docker/file_updater.rb +16 -231
- data/lib/dependabot/docker/update_checker.rb +10 -4
- data/lib/dependabot/shared/shared_file_fetcher.rb +99 -0
- data/lib/dependabot/shared/shared_file_parser.rb +80 -0
- data/lib/dependabot/shared/shared_file_updater.rb +261 -0
- data/lib/dependabot/{docker → shared}/utils/credentials_finder.rb +1 -1
- data/lib/dependabot/{docker → shared}/utils/helpers.rb +1 -1
- metadata +10 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cccf6c10f85241bf26410b82522a328174d17dcbb8d1a98bb9d840ab135660b2
|
4
|
+
data.tar.gz: 6796c6ab266df1313ee063ddb6c7c0b983b5297e3b5a0f30145604ae6cde9c9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a501a6453057650b26929251686eda18440fd30aec093cfcd7c84a8fa908ed0a1893fea3a3f6cb7078c1dad93d0ebe096c9fde3c8de06205627299567aadada
|
7
|
+
data.tar.gz: 823c1834d1723c2b970424acdd8f1ecd5ef02aa0013230a46fd6457eb74dc115c50e2b10d70117147fe3e425d4d128ecf8a0939d686f4d1124ff0e85f5fbae59
|
@@ -1,68 +1,63 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "
|
5
|
-
require "dependabot/
|
6
|
-
require "dependabot/file_fetchers"
|
7
|
-
require "dependabot/file_fetchers/base"
|
4
|
+
require "dependabot/shared/utils/helpers"
|
5
|
+
require "dependabot/shared/shared_file_fetcher"
|
8
6
|
|
9
7
|
module Dependabot
|
10
8
|
module Docker
|
11
|
-
class FileFetcher < Dependabot::
|
9
|
+
class FileFetcher < Dependabot::Shared::SharedFileFetcher
|
12
10
|
extend T::Sig
|
13
|
-
extend T::Helpers
|
14
11
|
|
15
|
-
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
16
12
|
DOCKER_REGEXP = /dockerfile|containerfile/i
|
17
13
|
|
14
|
+
sig { override.returns(Regexp) }
|
15
|
+
def self.filename_regex
|
16
|
+
DOCKER_REGEXP
|
17
|
+
end
|
18
|
+
|
19
|
+
sig { override.returns(String) }
|
20
|
+
def self.required_files_message
|
21
|
+
"Repo must contain a Dockerfile, Containerfile, or Kubernetes YAML files."
|
22
|
+
end
|
23
|
+
|
18
24
|
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
19
25
|
def self.required_files_in?(filenames)
|
20
26
|
filenames.any? { |f| f.match?(DOCKER_REGEXP) } or
|
21
27
|
filenames.any? { |f| f.match?(YAML_REGEXP) }
|
22
28
|
end
|
23
29
|
|
30
|
+
private
|
31
|
+
|
24
32
|
sig { override.returns(String) }
|
25
|
-
def
|
26
|
-
"
|
33
|
+
def default_file_name
|
34
|
+
"Dockerfile"
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { override.returns(String) }
|
38
|
+
def file_type
|
39
|
+
"Docker"
|
27
40
|
end
|
28
41
|
|
29
42
|
sig { override.returns(T::Array[DependencyFile]) }
|
30
43
|
def fetch_files
|
31
|
-
fetched_files =
|
32
|
-
fetched_files +=
|
33
|
-
fetched_files += correctly_encoded_yamlfiles
|
44
|
+
fetched_files = correctly_encoded_dockerfiles
|
45
|
+
fetched_files += super
|
34
46
|
|
35
47
|
return fetched_files if fetched_files.any?
|
36
48
|
|
37
|
-
|
38
|
-
raise Dependabot::DependencyFileNotFound.new(
|
39
|
-
File.join(directory, "Dockerfile"),
|
40
|
-
"No Dockerfiles nor Kubernetes YAML found in #{directory}"
|
41
|
-
)
|
42
|
-
elsif incorrectly_encoded_dockerfiles.none?
|
43
|
-
raise(
|
44
|
-
Dependabot::DependencyFileNotParseable,
|
45
|
-
T.must(incorrectly_encoded_yamlfiles.first).path
|
46
|
-
)
|
47
|
-
else
|
48
|
-
raise(
|
49
|
-
Dependabot::DependencyFileNotParseable,
|
50
|
-
T.must(incorrectly_encoded_dockerfiles.first).path
|
51
|
-
)
|
52
|
-
end
|
49
|
+
raise_appropriate_error(incorrectly_encoded_dockerfiles)
|
53
50
|
end
|
54
51
|
|
55
|
-
private
|
56
|
-
|
57
52
|
sig { returns(T::Array[DependencyFile]) }
|
58
53
|
def dockerfiles
|
59
|
-
@dockerfiles ||= T.let(
|
54
|
+
@dockerfiles ||= T.let(fetch_candidate_dockerfiles, T.nilable(T::Array[DependencyFile]))
|
60
55
|
end
|
61
56
|
|
62
|
-
sig { returns(T::Array[
|
63
|
-
def
|
57
|
+
sig { returns(T::Array[DependencyFile]) }
|
58
|
+
def fetch_candidate_dockerfiles
|
64
59
|
repo_contents(raise_errors: false)
|
65
|
-
.select { |f| f.type == "file" && f.name.match?(
|
60
|
+
.select { |f| f.type == "file" && f.name.match?(self.class.filename_regex) }
|
66
61
|
.map { |f| fetch_file_from_host(f.name) }
|
67
62
|
end
|
68
63
|
|
@@ -75,44 +70,6 @@ module Dependabot
|
|
75
70
|
def incorrectly_encoded_dockerfiles
|
76
71
|
dockerfiles.reject { |f| f.content&.valid_encoding? }
|
77
72
|
end
|
78
|
-
|
79
|
-
sig { returns(T::Array[DependencyFile]) }
|
80
|
-
def yamlfiles
|
81
|
-
@yamlfiles ||= T.let(
|
82
|
-
repo_contents(raise_errors: false)
|
83
|
-
.select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
84
|
-
.map { |f| fetch_file_from_host(f.name) },
|
85
|
-
T.nilable(T::Array[DependencyFile])
|
86
|
-
)
|
87
|
-
end
|
88
|
-
|
89
|
-
sig { params(resource: Object).returns(T.nilable(T::Boolean)) }
|
90
|
-
def likely_kubernetes_resource?(resource)
|
91
|
-
# Heuristic for being a Kubernetes resource. We could make this tighter but this probably works well.
|
92
|
-
resource.is_a?(::Hash) && resource.key?("apiVersion") && resource.key?("kind")
|
93
|
-
end
|
94
|
-
|
95
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
96
|
-
def correctly_encoded_yamlfiles
|
97
|
-
candidate_files = yamlfiles.select { |f| f.content&.valid_encoding? }
|
98
|
-
candidate_files.select do |f|
|
99
|
-
if f.type == "file" && Utils.likely_helm_chart?(f)
|
100
|
-
true
|
101
|
-
else
|
102
|
-
# This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
|
103
|
-
# in a multi-resource file had better be a valid k8s resource
|
104
|
-
content = ::YAML.safe_load(T.must(f.content), aliases: true)
|
105
|
-
likely_kubernetes_resource?(content)
|
106
|
-
end
|
107
|
-
rescue ::Psych::Exception
|
108
|
-
false
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
113
|
-
def incorrectly_encoded_yamlfiles
|
114
|
-
yamlfiles.reject { |f| f.content&.valid_encoding? }
|
115
|
-
end
|
116
73
|
end
|
117
74
|
end
|
118
75
|
end
|
@@ -1,47 +1,27 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "
|
5
|
-
|
6
|
-
require "dependabot/dependency"
|
7
|
-
require "dependabot/file_parsers"
|
8
|
-
require "dependabot/file_parsers/base"
|
9
|
-
require "dependabot/errors"
|
10
|
-
require "sorbet-runtime"
|
4
|
+
require "dependabot/shared/shared_file_parser"
|
11
5
|
require "dependabot/docker/package_manager"
|
12
6
|
|
13
7
|
module Dependabot
|
14
8
|
module Docker
|
15
|
-
class FileParser < Dependabot::
|
9
|
+
class FileParser < Dependabot::Shared::SharedFileParser
|
16
10
|
extend T::Sig
|
17
11
|
|
18
|
-
require "dependabot/file_parsers/base/dependency_set"
|
19
|
-
|
20
12
|
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
21
|
-
|
22
|
-
# Details of Docker regular expressions is at
|
23
|
-
# https://github.com/docker/distribution/blob/master/reference/regexp.go
|
24
|
-
DOMAIN_COMPONENT = /(?:[[:alnum:]]|[[:alnum:]][[[:alnum:]]-]*[[:alnum:]])/
|
25
|
-
DOMAIN = /(?:#{DOMAIN_COMPONENT}(?:\.#{DOMAIN_COMPONENT})+)/
|
26
|
-
REGISTRY = /(?<registry>#{DOMAIN}(?::\d+)?)/
|
27
|
-
|
28
|
-
NAME_COMPONENT = /(?:[a-z\d]+(?:(?:[._]|__|[-]*)[a-z\d]+)*)/
|
29
|
-
IMAGE = %r{(?<image>#{NAME_COMPONENT}(?:/#{NAME_COMPONENT})*)}
|
30
|
-
|
31
13
|
FROM = /FROM/i
|
32
14
|
PLATFORM = /--platform\=(?<platform>\S+)/
|
33
15
|
TAG_NO_PREFIX = /(?<tag>[\w][\w.-]{0,127})/
|
34
16
|
TAG = /:#{TAG_NO_PREFIX}/
|
35
17
|
DIGEST = /(?<digest>[0-9a-f]{64})/
|
36
|
-
|
18
|
+
|
37
19
|
FROM_LINE =
|
38
20
|
%r{^#{FROM}\s+(#{PLATFORM}\s+)?(#{REGISTRY}/)?
|
39
21
|
#{IMAGE}#{TAG}?(?:@sha256:#{DIGEST})?#{NAME}?}x
|
40
|
-
TAG_WITH_DIGEST = /^#{TAG_NO_PREFIX}(?:@sha256:#{DIGEST})?/x
|
41
|
-
|
42
|
-
AWS_ECR_URL = /dkr\.ecr\.(?<region>[^.]+)\.amazonaws\.com/
|
43
22
|
|
44
23
|
IMAGE_SPEC = %r{^(#{REGISTRY}/)?#{IMAGE}#{TAG}?(?:@sha256:#{DIGEST})?#{NAME}?}x
|
24
|
+
TAG_WITH_DIGEST = /^#{TAG_NO_PREFIX}(?:@sha256:#{DIGEST})?/x
|
45
25
|
|
46
26
|
sig { returns(Ecosystem) }
|
47
27
|
def ecosystem
|
@@ -54,7 +34,6 @@ module Dependabot
|
|
54
34
|
)
|
55
35
|
end
|
56
36
|
|
57
|
-
# rubocop:disable Metrics/AbcSize
|
58
37
|
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
59
38
|
def parse
|
60
39
|
dependency_set = DependencySet.new
|
@@ -69,75 +48,55 @@ module Dependabot
|
|
69
48
|
version = version_from(parsed_from_line)
|
70
49
|
next unless version
|
71
50
|
|
72
|
-
dependency_set <<
|
73
|
-
name: T.must(parsed_from_line.fetch("image")),
|
74
|
-
version: version,
|
75
|
-
package_manager: "docker",
|
76
|
-
requirements: [
|
77
|
-
requirement: nil,
|
78
|
-
groups: [],
|
79
|
-
file: dockerfile.name,
|
80
|
-
source: source_from(parsed_from_line)
|
81
|
-
]
|
82
|
-
)
|
51
|
+
dependency_set << build_dependency(dockerfile, parsed_from_line, version)
|
83
52
|
end
|
84
53
|
end
|
85
54
|
|
86
55
|
manifest_files.each do |file|
|
87
|
-
|
88
|
-
# 0xFEFF is the encoding for the byte order mark (BOM). If a YAML file is loaded with a BOM it will parse
|
89
|
-
# successfully, but will only load the first line. To prevent this nearly empty object from being returned,
|
90
|
-
# the BOM is manually detected and reported as a parse error.
|
91
|
-
file_path = Pathname.new(file.directory).join(file.name).cleanpath.to_path
|
92
|
-
msg = "The file appears to have been saved with a byte order mark (BOM). This will prevent proper parsing."
|
93
|
-
raise Dependabot::DependencyFileNotParseable.new(file_path, msg)
|
94
|
-
end
|
56
|
+
check_manifest_file_encoding(file)
|
95
57
|
dependency_set += workfile_file_dependencies(file)
|
96
58
|
end
|
97
59
|
|
98
60
|
dependency_set.dependencies
|
99
61
|
end
|
100
|
-
# rubocop:enable Metrics/AbcSize
|
101
62
|
|
102
63
|
private
|
103
64
|
|
65
|
+
sig { override.returns(String) }
|
66
|
+
def package_manager
|
67
|
+
"docker"
|
68
|
+
end
|
69
|
+
|
70
|
+
sig { override.returns(String) }
|
71
|
+
def file_type
|
72
|
+
"Dockerfile"
|
73
|
+
end
|
74
|
+
|
104
75
|
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
105
76
|
def dockerfiles
|
106
77
|
# The Docker file fetcher fetches Dockerfiles and yaml files. Reject yaml files.
|
107
78
|
dependency_files.reject { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
108
79
|
end
|
109
80
|
|
110
|
-
sig {
|
111
|
-
def
|
112
|
-
|
113
|
-
end
|
114
|
-
|
115
|
-
sig { params(parsed_from_line: T::Hash[String, T.nilable(String)]).returns(T::Hash[String, T.nilable(String)]) }
|
116
|
-
def source_from(parsed_from_line)
|
117
|
-
source = {}
|
118
|
-
|
119
|
-
source[:registry] = parsed_from_line.fetch("registry") if parsed_from_line.fetch("registry")
|
120
|
-
|
121
|
-
source[:tag] = parsed_from_line.fetch("tag") if parsed_from_line.fetch("tag")
|
122
|
-
|
123
|
-
source[:digest] = parsed_from_line.fetch("digest") if parsed_from_line.fetch("digest")
|
124
|
-
|
125
|
-
source
|
81
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
82
|
+
def manifest_files
|
83
|
+
dependency_files.select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
126
84
|
end
|
127
85
|
|
128
|
-
sig {
|
129
|
-
def
|
130
|
-
|
131
|
-
return if dependency_files.any?
|
86
|
+
sig { params(file: Dependabot::DependencyFile).void }
|
87
|
+
def check_manifest_file_encoding(file)
|
88
|
+
return unless file.content&.start_with?("\uFEFF")
|
132
89
|
|
133
|
-
|
90
|
+
file_path = Pathname.new(file.directory).join(file.name).cleanpath.to_path
|
91
|
+
msg = "The file appears to have been saved with a byte order mark (BOM). This will prevent proper parsing."
|
92
|
+
raise Dependabot::DependencyFileNotParseable.new(file_path, msg)
|
134
93
|
end
|
135
94
|
|
136
|
-
sig { params(file:
|
95
|
+
sig { params(file: Dependabot::DependencyFile).returns(DependencySet) }
|
137
96
|
def workfile_file_dependencies(file)
|
138
97
|
dependency_set = DependencySet.new
|
139
98
|
|
140
|
-
resources = file.content.split(/^---$/).map(&:strip).reject(&:empty?)
|
99
|
+
resources = T.must(file.content).split(/^---$/).map(&:strip).reject(&:empty?)
|
141
100
|
resources.flat_map do |resource|
|
142
101
|
json = YAML.safe_load(resource, aliases: true)
|
143
102
|
images = deep_fetch_images(json).uniq
|
@@ -152,7 +111,7 @@ module Dependabot
|
|
152
111
|
version = version_from(details)
|
153
112
|
next unless version
|
154
113
|
|
155
|
-
dependency_set <<
|
114
|
+
dependency_set << build_dependency(file, details, version)
|
156
115
|
end
|
157
116
|
end
|
158
117
|
|
@@ -161,25 +120,7 @@ module Dependabot
|
|
161
120
|
raise Dependabot::DependencyFileNotParseable, file.path
|
162
121
|
end
|
163
122
|
|
164
|
-
sig
|
165
|
-
params(file: T.untyped, details: T.untyped,
|
166
|
-
version: T.nilable(T.any(String, Dependabot::Version))).returns(Dependabot::Dependency)
|
167
|
-
end
|
168
|
-
def build_image_dependency(file, details, version)
|
169
|
-
Dependency.new(
|
170
|
-
name: details.fetch("image"),
|
171
|
-
version: version,
|
172
|
-
package_manager: "docker",
|
173
|
-
requirements: [
|
174
|
-
requirement: nil,
|
175
|
-
groups: [],
|
176
|
-
file: file.name,
|
177
|
-
source: source_from(details)
|
178
|
-
]
|
179
|
-
)
|
180
|
-
end
|
181
|
-
|
182
|
-
sig { params(json_obj: T.anything).returns(T.untyped) }
|
123
|
+
sig { params(json_obj: T.anything).returns(T::Array[String]) }
|
183
124
|
def deep_fetch_images(json_obj)
|
184
125
|
case json_obj
|
185
126
|
when Hash then deep_fetch_images_from_hash(json_obj)
|
@@ -188,7 +129,7 @@ module Dependabot
|
|
188
129
|
end
|
189
130
|
end
|
190
131
|
|
191
|
-
sig { params(json_object: T.untyped).returns(T::Array[
|
132
|
+
sig { params(json_object: T::Hash[T.untyped, T.untyped]).returns(T::Array[String]) }
|
192
133
|
def deep_fetch_images_from_hash(json_object)
|
193
134
|
img = json_object.fetch("image", nil)
|
194
135
|
|
@@ -204,12 +145,6 @@ module Dependabot
|
|
204
145
|
images + json_object.values.flat_map { |obj| deep_fetch_images(obj) }
|
205
146
|
end
|
206
147
|
|
207
|
-
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
208
|
-
def manifest_files
|
209
|
-
# Dependencies include both Dockerfiles and yaml, select yaml.
|
210
|
-
dependency_files.select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
211
|
-
end
|
212
|
-
|
213
148
|
sig { params(img_hash: T::Hash[String, T.nilable(String)]).returns(T::Array[String]) }
|
214
149
|
def parse_helm(img_hash)
|
215
150
|
tag_value = img_hash.key?("tag") ? img_hash.fetch("tag", nil) : img_hash.fetch("version", nil)
|
@@ -230,6 +165,13 @@ module Dependabot
|
|
230
165
|
image << "@sha256:#{digest}/" if digest
|
231
166
|
[image]
|
232
167
|
end
|
168
|
+
|
169
|
+
sig { override.void }
|
170
|
+
def check_required_files
|
171
|
+
return if dependency_files.any?
|
172
|
+
|
173
|
+
raise "No #{file_type}!"
|
174
|
+
end
|
233
175
|
end
|
234
176
|
end
|
235
177
|
end
|
@@ -1,253 +1,38 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strong
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require "dependabot/docker/utils/helpers"
|
5
|
-
require "dependabot/file_updaters"
|
6
|
-
require "dependabot/file_updaters/base"
|
7
|
-
require "dependabot/errors"
|
8
4
|
require "sorbet-runtime"
|
5
|
+
require "dependabot/file_fetchers"
|
6
|
+
require "dependabot/file_fetchers/base"
|
7
|
+
require "dependabot/shared/shared_file_updater"
|
9
8
|
|
10
9
|
module Dependabot
|
11
10
|
module Docker
|
12
|
-
class FileUpdater < Dependabot::
|
11
|
+
class FileUpdater < Dependabot::Shared::SharedFileUpdater
|
13
12
|
extend T::Sig
|
14
13
|
|
15
|
-
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i
|
16
|
-
|
17
14
|
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
18
15
|
DOCKER_REGEXP = /(docker|container)file/i
|
16
|
+
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i
|
19
17
|
|
20
18
|
sig { override.returns(T::Array[Regexp]) }
|
21
19
|
def self.updated_files_regex
|
22
|
-
[
|
23
|
-
DOCKER_REGEXP,
|
24
|
-
YAML_REGEXP
|
25
|
-
]
|
26
|
-
end
|
27
|
-
|
28
|
-
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
29
|
-
def updated_dependency_files
|
30
|
-
updated_files = []
|
31
|
-
dependency_files.each do |file|
|
32
|
-
next unless requirement_changed?(file, T.must(dependency))
|
33
|
-
|
34
|
-
updated_files << if file.name.match?(YAML_REGEXP)
|
35
|
-
updated_file(
|
36
|
-
file: file,
|
37
|
-
content: T.must(updated_yaml_content(file))
|
38
|
-
)
|
39
|
-
else
|
40
|
-
updated_file(
|
41
|
-
file: file,
|
42
|
-
content: updated_dockerfile_content(file)
|
43
|
-
)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
updated_files.reject! { |f| dependency_files.include?(f) }
|
48
|
-
raise "No files changed!" if updated_files.none?
|
49
|
-
|
50
|
-
updated_files
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
sig { returns T.nilable(Dependabot::Dependency) }
|
56
|
-
def dependency
|
57
|
-
# Dockerfiles will only ever be updating a single dependency
|
58
|
-
dependencies.first
|
59
|
-
end
|
60
|
-
|
61
|
-
sig { override.void }
|
62
|
-
def check_required_files
|
63
|
-
# Just check if there are any files at all.
|
64
|
-
return if dependency_files.any?
|
65
|
-
|
66
|
-
raise "No Dockerfile or Containerfile!"
|
67
|
-
end
|
68
|
-
|
69
|
-
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
70
|
-
def updated_dockerfile_content(file)
|
71
|
-
old_sources = previous_sources(file)
|
72
|
-
new_sources = sources(file)
|
73
|
-
|
74
|
-
updated_content = T.let(file.content, T.untyped)
|
75
|
-
|
76
|
-
T.must(old_sources).zip(new_sources).each do |old_source, new_source|
|
77
|
-
updated_content = update_digest_and_tag(updated_content, old_source, T.must(new_source))
|
78
|
-
end
|
79
|
-
|
80
|
-
raise "Expected content to change!" if updated_content == file.content
|
81
|
-
|
82
|
-
updated_content
|
83
|
-
end
|
84
|
-
|
85
|
-
sig do
|
86
|
-
params(previous_content: String, old_source: T::Hash[Symbol, T.nilable(String)],
|
87
|
-
new_source: T::Hash[Symbol, T.nilable(String)]).returns(String)
|
88
|
-
end
|
89
|
-
def update_digest_and_tag(previous_content, old_source, new_source)
|
90
|
-
old_digest = old_source[:digest]
|
91
|
-
new_digest = new_source[:digest]
|
92
|
-
|
93
|
-
old_tag = old_source[:tag]
|
94
|
-
new_tag = new_source[:tag]
|
95
|
-
|
96
|
-
old_declaration =
|
97
|
-
if private_registry_url(old_source) then "#{private_registry_url(old_source)}/"
|
98
|
-
else
|
99
|
-
""
|
100
|
-
end
|
101
|
-
old_declaration += T.must(dependency).name
|
102
|
-
old_declaration +=
|
103
|
-
if specified_with_tag?(old_source) then ":#{old_tag}"
|
104
|
-
else
|
105
|
-
""
|
106
|
-
end
|
107
|
-
old_declaration +=
|
108
|
-
if specified_with_digest?(old_source) then "@sha256:#{old_digest}"
|
109
|
-
else
|
110
|
-
""
|
111
|
-
end
|
112
|
-
escaped_declaration = Regexp.escape(old_declaration)
|
113
|
-
|
114
|
-
old_declaration_regex =
|
115
|
-
%r{^#{FROM_REGEX}\s+(docker\.io/)?#{escaped_declaration}(?=\s|$)}
|
116
|
-
|
117
|
-
previous_content.gsub(old_declaration_regex) do |old_dec|
|
118
|
-
old_dec
|
119
|
-
.gsub("@sha256:#{old_digest}", "@sha256:#{new_digest}")
|
120
|
-
.gsub(":#{old_tag}", ":#{new_tag}")
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
|
125
|
-
def specified_with_tag?(source)
|
126
|
-
source[:tag]
|
127
|
-
end
|
128
|
-
|
129
|
-
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
|
130
|
-
def specified_with_digest?(source)
|
131
|
-
source[:digest]
|
132
|
-
end
|
133
|
-
|
134
|
-
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[String])) }
|
135
|
-
def new_tags(file)
|
136
|
-
requirements(file)
|
137
|
-
.map { |r| r.fetch(:source)[:tag] }
|
138
|
-
end
|
139
|
-
|
140
|
-
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[String])) }
|
141
|
-
def old_tags(file)
|
142
|
-
previous_requirements(file)
|
143
|
-
&.map { |r| r.fetch(:source)[:tag] }
|
144
|
-
end
|
145
|
-
|
146
|
-
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
|
147
|
-
def private_registry_url(source)
|
148
|
-
source[:registry]
|
149
|
-
end
|
150
|
-
|
151
|
-
sig { params(file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, T.nilable(String)]]) }
|
152
|
-
def sources(file)
|
153
|
-
requirements(file).map { |r| r.fetch(:source) }
|
154
|
-
end
|
155
|
-
|
156
|
-
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[T::Hash[Symbol, T.nilable(String)]])) }
|
157
|
-
def previous_sources(file)
|
158
|
-
previous_requirements(file)&.map { |r| r.fetch(:source) }
|
159
|
-
end
|
160
|
-
|
161
|
-
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
162
|
-
def updated_yaml_content(file)
|
163
|
-
updated_content = file.content
|
164
|
-
updated_content = update_helm(file, updated_content) if Utils.likely_helm_chart?(file)
|
165
|
-
updated_content = update_image(file, updated_content)
|
166
|
-
|
167
|
-
raise "Expected content to change!" if updated_content == file.content
|
168
|
-
|
169
|
-
updated_content
|
170
|
-
end
|
171
|
-
|
172
|
-
sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
|
173
|
-
def update_helm(file, content)
|
174
|
-
# TODO: this won't work if two images have the same tag version
|
175
|
-
old_tags = old_helm_tags(file)
|
176
|
-
return if old_tags.empty?
|
177
|
-
|
178
|
-
modified_content = content
|
179
|
-
|
180
|
-
old_tags.each do |old_tag|
|
181
|
-
old_tag_regex = /^\s+(?:-\s)?(?:tag|version):\s+["']?#{old_tag}["']?(?=\s|$)/
|
182
|
-
modified_content = modified_content&.gsub(old_tag_regex) do |old_img_tag|
|
183
|
-
old_img_tag.gsub(old_tag.to_s, new_helm_tag(file).to_s)
|
184
|
-
end
|
185
|
-
end
|
186
|
-
modified_content
|
187
|
-
end
|
188
|
-
|
189
|
-
sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
|
190
|
-
def update_image(file, content)
|
191
|
-
old_images = old_yaml_images(file)
|
192
|
-
return if old_images.empty?
|
193
|
-
|
194
|
-
modified_content = content
|
195
|
-
|
196
|
-
old_images.each do |old_image|
|
197
|
-
old_image_regex = /^\s*(?:-\s)?image:\s+#{old_image}(?=\s|$)/
|
198
|
-
modified_content = modified_content&.gsub(old_image_regex) do |old_img|
|
199
|
-
old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
|
200
|
-
end
|
201
|
-
end
|
202
|
-
modified_content
|
203
|
-
end
|
204
|
-
|
205
|
-
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
206
|
-
def new_yaml_image(file)
|
207
|
-
element = T.must(dependency).requirements.find { |r| r[:file] == file.name }
|
208
|
-
prefix = element&.dig(:source, :registry) ? "#{element.fetch(:source)[:registry]}/" : ""
|
209
|
-
digest = element&.dig(:source, :digest) ? "@sha256:#{element.fetch(:source)[:digest]}" : ""
|
210
|
-
tag = element&.dig(:source, :tag) ? ":#{element.fetch(:source)[:tag]}" : ""
|
211
|
-
"#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
|
212
|
-
end
|
213
|
-
|
214
|
-
sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
215
|
-
def old_yaml_images(file)
|
216
|
-
T.must(previous_requirements(file)).map do |r|
|
217
|
-
prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
|
218
|
-
digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
|
219
|
-
tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
|
220
|
-
"#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
225
|
-
def old_helm_tags(file)
|
226
|
-
T.must(previous_requirements(file)).map do |r|
|
227
|
-
tag = r.fetch(:source)[:tag] || ""
|
228
|
-
digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
|
229
|
-
"#{tag}#{digest}"
|
230
|
-
end
|
20
|
+
[DOCKER_REGEXP, YAML_REGEXP]
|
231
21
|
end
|
232
22
|
|
233
|
-
sig {
|
234
|
-
def
|
235
|
-
|
236
|
-
tag = T.must(element).dig(:source, :tag) || ""
|
237
|
-
digest = T.must(element).dig(:source, :digest) ? "@sha256:#{T.must(element).dig(:source, :digest)}" : ""
|
238
|
-
"#{tag}#{digest}"
|
23
|
+
sig { override.returns(String) }
|
24
|
+
def file_type
|
25
|
+
"Dockerfile or Containerfile"
|
239
26
|
end
|
240
27
|
|
241
|
-
sig {
|
242
|
-
def
|
243
|
-
|
244
|
-
.select { |r| r[:file] == file.name }
|
28
|
+
sig { override.returns(Regexp) }
|
29
|
+
def yaml_file_pattern
|
30
|
+
YAML_REGEXP
|
245
31
|
end
|
246
32
|
|
247
|
-
sig {
|
248
|
-
def
|
249
|
-
|
250
|
-
&.select { |r| r[:file] == file.name }
|
33
|
+
sig { override.returns(Regexp) }
|
34
|
+
def container_image_regex
|
35
|
+
%r{^#{FROM_REGEX}\s+(docker\.io/)?}o
|
251
36
|
end
|
252
37
|
end
|
253
38
|
end
|
@@ -11,7 +11,7 @@ require "dependabot/docker/tag"
|
|
11
11
|
require "dependabot/docker/file_parser"
|
12
12
|
require "dependabot/docker/version"
|
13
13
|
require "dependabot/docker/requirement"
|
14
|
-
require "dependabot/
|
14
|
+
require "dependabot/shared/utils/credentials_finder"
|
15
15
|
|
16
16
|
module Dependabot
|
17
17
|
module Docker
|
@@ -261,6 +261,12 @@ module Dependabot
|
|
261
261
|
rescue RestClient::ServerBrokeConnection,
|
262
262
|
RestClient::TooManyRequests
|
263
263
|
raise PrivateSourceBadResponse, registry_hostname
|
264
|
+
rescue JSON::ParserError => e
|
265
|
+
if e.message.include?("unexpected token")
|
266
|
+
raise DependencyFileNotResolvable, "Error while accessing docker image at #{registry_hostname}"
|
267
|
+
end
|
268
|
+
|
269
|
+
raise
|
264
270
|
end
|
265
271
|
|
266
272
|
sig { returns(T.nilable(String)) }
|
@@ -355,11 +361,11 @@ module Dependabot
|
|
355
361
|
credentials_finder.credentials_for_registry(registry_hostname)
|
356
362
|
end
|
357
363
|
|
358
|
-
sig { returns(Dependabot::
|
364
|
+
sig { returns(Dependabot::Shared::Utils::CredentialsFinder) }
|
359
365
|
def credentials_finder
|
360
366
|
@credentials_finder ||= T.let(
|
361
|
-
Utils::CredentialsFinder.new(credentials),
|
362
|
-
T.nilable(Dependabot::
|
367
|
+
Dependabot::Shared::Utils::CredentialsFinder.new(credentials),
|
368
|
+
T.nilable(Dependabot::Shared::Utils::CredentialsFinder)
|
363
369
|
)
|
364
370
|
end
|
365
371
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
require "dependabot/file_fetchers"
|
6
|
+
require "dependabot/file_fetchers/base"
|
7
|
+
require "dependabot/shared/utils/helpers"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module Shared
|
11
|
+
class SharedFileFetcher < Dependabot::FileFetchers::Base
|
12
|
+
extend T::Sig
|
13
|
+
extend T::Helpers
|
14
|
+
|
15
|
+
abstract!
|
16
|
+
|
17
|
+
YAML_REGEXP = /^[^\.].*\.ya?ml$/i
|
18
|
+
|
19
|
+
sig { abstract.returns(Regexp) }
|
20
|
+
def self.filename_regex; end
|
21
|
+
|
22
|
+
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
23
|
+
def self.required_files_in?(filenames)
|
24
|
+
filenames.any? { |f| f.match?(filename_regex) }
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { override.returns(T::Array[DependencyFile]) }
|
28
|
+
def fetch_files
|
29
|
+
fetched_files = []
|
30
|
+
fetched_files + correctly_encoded_yamlfiles
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
34
|
+
def correctly_encoded_yamlfiles
|
35
|
+
candidate_files = yamlfiles.select { |f| f.content&.valid_encoding? }
|
36
|
+
candidate_files.select do |f|
|
37
|
+
if f.type == "file" && Utils.likely_helm_chart?(f)
|
38
|
+
true
|
39
|
+
else
|
40
|
+
# This doesn't handle multi-resource files, but it shouldn't matter, since the first resource
|
41
|
+
# in a multi-resource file had better be a valid k8s resource
|
42
|
+
content = YAML.safe_load(T.must(f.content), aliases: true)
|
43
|
+
likely_kubernetes_resource?(content)
|
44
|
+
end
|
45
|
+
rescue ::Psych::Exception
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
51
|
+
def incorrectly_encoded_yamlfiles
|
52
|
+
yamlfiles.reject { |f| f.content&.valid_encoding? }
|
53
|
+
end
|
54
|
+
|
55
|
+
sig do
|
56
|
+
params(
|
57
|
+
incorrectly_encoded_files: T::Array[Dependabot::DependencyFile]
|
58
|
+
).returns(T.noreturn)
|
59
|
+
end
|
60
|
+
def raise_appropriate_error(
|
61
|
+
incorrectly_encoded_files = []
|
62
|
+
)
|
63
|
+
if incorrectly_encoded_files.none? && incorrectly_encoded_yamlfiles.none?
|
64
|
+
raise Dependabot::DependencyFileNotFound.new(
|
65
|
+
File.join(directory, "Dockerfile"),
|
66
|
+
"No Dockerfiles nor Kubernetes YAML found in #{directory}"
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
invalid_files = incorrectly_encoded_files.any? ? incorrectly_encoded_files : incorrectly_encoded_yamlfiles
|
71
|
+
raise Dependabot::DependencyFileNotParseable, T.must(invalid_files.first).path
|
72
|
+
end
|
73
|
+
|
74
|
+
sig { returns(T::Array[DependencyFile]) }
|
75
|
+
def yamlfiles
|
76
|
+
@yamlfiles ||= T.let(
|
77
|
+
repo_contents(raise_errors: false)
|
78
|
+
.select { |f| f.type == "file" && f.name.match?(YAML_REGEXP) }
|
79
|
+
.map { |f| fetch_file_from_host(f.name) },
|
80
|
+
T.nilable(T::Array[DependencyFile])
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
sig { params(resource: Object).returns(T.nilable(T::Boolean)) }
|
87
|
+
def likely_kubernetes_resource?(resource)
|
88
|
+
# Heuristic for being a Kubernetes resource. We could make this tighter but this probably works well.
|
89
|
+
resource.is_a?(::Hash) && resource.key?("apiVersion") && resource.key?("kind")
|
90
|
+
end
|
91
|
+
|
92
|
+
sig { abstract.returns(String) }
|
93
|
+
def default_file_name; end
|
94
|
+
|
95
|
+
sig { abstract.returns(String) }
|
96
|
+
def file_type; end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/dependency"
|
5
|
+
require "dependabot/file_parsers"
|
6
|
+
require "dependabot/file_parsers/base"
|
7
|
+
require "sorbet-runtime"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module Shared
|
11
|
+
class SharedFileParser < Dependabot::FileParsers::Base
|
12
|
+
extend T::Sig
|
13
|
+
extend T::Helpers
|
14
|
+
|
15
|
+
abstract!
|
16
|
+
|
17
|
+
require "dependabot/file_parsers/base/dependency_set"
|
18
|
+
|
19
|
+
# Details of Docker regular expressions is at
|
20
|
+
# https://github.com/docker/distribution/blob/master/reference/regexp.go
|
21
|
+
DOMAIN_COMPONENT = /(?:[[:alnum:]]|[[:alnum:]][[[:alnum:]]-]*[[:alnum:]])/
|
22
|
+
DOMAIN = /(?:#{DOMAIN_COMPONENT}(?:\.#{DOMAIN_COMPONENT})+)/
|
23
|
+
REGISTRY = /(?<registry>#{DOMAIN}(?::\d+)?)/
|
24
|
+
|
25
|
+
NAME_COMPONENT = /(?:[a-z\d]+(?:(?:[._]|__|[-]*)[a-z\d]+)*)/
|
26
|
+
IMAGE = %r{(?<image>#{NAME_COMPONENT}(?:/#{NAME_COMPONENT})*)}
|
27
|
+
|
28
|
+
TAG = /:(?<tag>[\w][\w.-]{0,127})/
|
29
|
+
DIGEST = /@(?<digest>[^\s]+)/
|
30
|
+
NAME = /\s+AS\s+(?<name>[\w-]+)/
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
sig { params(parsed_line: T::Hash[String, T.nilable(String)]).returns(T.nilable(String)) }
|
35
|
+
def version_from(parsed_line)
|
36
|
+
parsed_line.fetch("tag") || parsed_line.fetch("digest")
|
37
|
+
end
|
38
|
+
|
39
|
+
sig { params(parsed_line: T::Hash[String, T.nilable(String)]).returns(T::Hash[String, T.nilable(String)]) }
|
40
|
+
def source_from(parsed_line)
|
41
|
+
source = {}
|
42
|
+
|
43
|
+
source[:registry] = parsed_line.fetch("registry") if parsed_line.fetch("registry")
|
44
|
+
source[:tag] = parsed_line.fetch("tag") if parsed_line.fetch("tag")
|
45
|
+
source[:digest] = parsed_line.fetch("digest") if parsed_line.fetch("digest")
|
46
|
+
|
47
|
+
source
|
48
|
+
end
|
49
|
+
|
50
|
+
sig do
|
51
|
+
params(file: Dependabot::DependencyFile, details: T::Hash[String, T.nilable(String)],
|
52
|
+
version: String).returns(Dependabot::Dependency)
|
53
|
+
end
|
54
|
+
def build_dependency(file, details, version)
|
55
|
+
Dependency.new(
|
56
|
+
name: T.must(details.fetch("image")),
|
57
|
+
version: version,
|
58
|
+
package_manager: package_manager,
|
59
|
+
requirements: [
|
60
|
+
requirement: nil,
|
61
|
+
groups: [],
|
62
|
+
file: file.name,
|
63
|
+
source: source_from(details)
|
64
|
+
]
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
sig { override.void }
|
71
|
+
def check_required_files; end
|
72
|
+
|
73
|
+
sig { abstract.returns(String) }
|
74
|
+
def package_manager; end
|
75
|
+
|
76
|
+
sig { abstract.returns(String) }
|
77
|
+
def file_type; end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/file_updaters"
|
5
|
+
require "dependabot/file_updaters/base"
|
6
|
+
require "dependabot/errors"
|
7
|
+
require "sorbet-runtime"
|
8
|
+
require "dependabot/shared/utils/helpers"
|
9
|
+
|
10
|
+
module Dependabot
|
11
|
+
module Shared
|
12
|
+
class SharedFileUpdater < Dependabot::FileUpdaters::Base
|
13
|
+
extend T::Sig
|
14
|
+
extend T::Helpers
|
15
|
+
|
16
|
+
abstract!
|
17
|
+
|
18
|
+
FROM_REGEX = /FROM(\s+--platform\=\S+)?/i
|
19
|
+
|
20
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
21
|
+
def updated_dependency_files
|
22
|
+
updated_files = []
|
23
|
+
dependency_files.each do |file|
|
24
|
+
next unless requirement_changed?(file, T.must(dependency))
|
25
|
+
|
26
|
+
updated_files << if file.name.match?(T.must(yaml_file_pattern))
|
27
|
+
updated_file(
|
28
|
+
file: file,
|
29
|
+
content: T.must(updated_yaml_content(file))
|
30
|
+
)
|
31
|
+
else
|
32
|
+
updated_file(
|
33
|
+
file: file,
|
34
|
+
content: T.must(updated_dockerfile_content(file))
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
updated_files.reject! { |f| dependency_files.include?(f) }
|
40
|
+
raise "No files changed!" if updated_files.none?
|
41
|
+
|
42
|
+
updated_files
|
43
|
+
end
|
44
|
+
|
45
|
+
sig { abstract.returns(T.nilable(Regexp)) }
|
46
|
+
def yaml_file_pattern; end
|
47
|
+
|
48
|
+
sig { abstract.returns(T.nilable(Regexp)) }
|
49
|
+
def container_image_regex; end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
54
|
+
def updated_dockerfile_content(file)
|
55
|
+
old_sources = previous_sources(file)
|
56
|
+
new_sources = sources(file)
|
57
|
+
|
58
|
+
updated_content = T.let(file.content, T.untyped)
|
59
|
+
|
60
|
+
T.must(old_sources).zip(new_sources).each do |old_source, new_source|
|
61
|
+
updated_content = update_digest_and_tag(updated_content, old_source, T.must(new_source))
|
62
|
+
end
|
63
|
+
|
64
|
+
raise "Expected content to change!" if updated_content == file.content
|
65
|
+
|
66
|
+
updated_content
|
67
|
+
end
|
68
|
+
|
69
|
+
sig do
|
70
|
+
params(previous_content: String, old_source: T::Hash[Symbol, T.nilable(String)],
|
71
|
+
new_source: T::Hash[Symbol, T.nilable(String)]).returns(String)
|
72
|
+
end
|
73
|
+
def update_digest_and_tag(previous_content, old_source, new_source)
|
74
|
+
old_digest = old_source[:digest]
|
75
|
+
new_digest = new_source[:digest]
|
76
|
+
|
77
|
+
old_tag = old_source[:tag]
|
78
|
+
new_tag = new_source[:tag]
|
79
|
+
|
80
|
+
old_declaration =
|
81
|
+
if private_registry_url(old_source)
|
82
|
+
"#{private_registry_url(old_source)}/"
|
83
|
+
else
|
84
|
+
""
|
85
|
+
end
|
86
|
+
old_declaration += T.must(dependency).name
|
87
|
+
old_declaration +=
|
88
|
+
if specified_with_tag?(old_source)
|
89
|
+
":#{old_tag}"
|
90
|
+
else
|
91
|
+
""
|
92
|
+
end
|
93
|
+
old_declaration +=
|
94
|
+
if specified_with_digest?(old_source)
|
95
|
+
"@sha256:#{old_digest}"
|
96
|
+
else
|
97
|
+
""
|
98
|
+
end
|
99
|
+
|
100
|
+
escaped_declaration = Regexp.escape(old_declaration)
|
101
|
+
|
102
|
+
old_declaration_regex = build_old_declaration_regex(escaped_declaration)
|
103
|
+
|
104
|
+
previous_content.gsub(old_declaration_regex) do |old_dec|
|
105
|
+
old_dec
|
106
|
+
.gsub("@sha256:#{old_digest}", "@sha256:#{new_digest}")
|
107
|
+
.gsub(":#{old_tag}", ":#{new_tag}")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
sig { params(escaped_declaration: String).returns(Regexp) }
|
112
|
+
def build_old_declaration_regex(escaped_declaration)
|
113
|
+
%r{^#{FROM_REGEX}\s+(docker\.io/)?#{escaped_declaration}(?=\s|$)}
|
114
|
+
end
|
115
|
+
|
116
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(String)) }
|
117
|
+
def updated_yaml_content(file)
|
118
|
+
updated_content = file.content
|
119
|
+
updated_content = update_helm(file, updated_content) if Shared::Utils.likely_helm_chart?(file)
|
120
|
+
updated_content = update_image(file, updated_content)
|
121
|
+
|
122
|
+
raise "Expected content to change!" if updated_content == file.content
|
123
|
+
|
124
|
+
updated_content
|
125
|
+
end
|
126
|
+
|
127
|
+
sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
|
128
|
+
def update_helm(file, content)
|
129
|
+
old_tags = old_helm_tags(file)
|
130
|
+
return if old_tags.empty?
|
131
|
+
|
132
|
+
modified_content = content
|
133
|
+
|
134
|
+
old_tags.each do |old_tag|
|
135
|
+
old_tag_regex = /^\s*(?:-\s)?(?:tag|version):\s+["']?#{old_tag}["']?(?=\s|$)/
|
136
|
+
modified_content = modified_content&.gsub(old_tag_regex) do |old_img_tag|
|
137
|
+
old_img_tag.gsub(old_tag.to_s, new_helm_tag(file).to_s)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
modified_content
|
141
|
+
end
|
142
|
+
|
143
|
+
sig { params(file: Dependabot::DependencyFile, content: T.nilable(String)).returns(T.nilable(String)) }
|
144
|
+
def update_image(file, content)
|
145
|
+
old_images = old_yaml_images(file)
|
146
|
+
return if old_images.empty?
|
147
|
+
|
148
|
+
modified_content = content
|
149
|
+
|
150
|
+
old_images.each do |old_image|
|
151
|
+
old_image_regex = /^\s*(?:-\s)?image:\s+#{old_image}(?=\s|$)/
|
152
|
+
modified_content = modified_content&.gsub(old_image_regex) do |old_img|
|
153
|
+
old_img.gsub(old_image.to_s, new_yaml_image(file).to_s)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
modified_content
|
157
|
+
end
|
158
|
+
|
159
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
160
|
+
def new_yaml_image(file)
|
161
|
+
element = T.must(dependency).requirements.find { |r| r[:file] == file.name }
|
162
|
+
prefix = element&.dig(:source, :registry) ? "#{element.fetch(:source)[:registry]}/" : ""
|
163
|
+
digest = element&.dig(:source, :digest) ? "@sha256:#{element.fetch(:source)[:digest]}" : ""
|
164
|
+
tag = element&.dig(:source, :tag) ? ":#{element.fetch(:source)[:tag]}" : ""
|
165
|
+
"#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
|
166
|
+
end
|
167
|
+
|
168
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
169
|
+
def old_yaml_images(file)
|
170
|
+
T.must(previous_requirements(file)).map do |r|
|
171
|
+
prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
|
172
|
+
digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
|
173
|
+
tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
|
174
|
+
"#{prefix}#{T.must(dependency).name}#{tag}#{digest}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[String]) }
|
179
|
+
def old_helm_tags(file)
|
180
|
+
T.must(previous_requirements(file)).map do |r|
|
181
|
+
tag = r.fetch(:source)[:tag] || ""
|
182
|
+
digest = r.fetch(:source)[:digest] ? "@sha256:#{r.fetch(:source)[:digest]}" : ""
|
183
|
+
"#{tag}#{digest}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
sig { params(file: Dependabot::DependencyFile).returns(String) }
|
188
|
+
def new_helm_tag(file)
|
189
|
+
element = T.must(dependency).requirements.find { |r| r[:file] == file.name }
|
190
|
+
tag = T.must(element).dig(:source, :tag) || ""
|
191
|
+
digest = T.must(element).dig(:source, :digest) ? "@sha256:#{T.must(element).dig(:source, :digest)}" : ""
|
192
|
+
"#{tag}#{digest}"
|
193
|
+
end
|
194
|
+
|
195
|
+
protected
|
196
|
+
|
197
|
+
sig { params(file: Dependabot::DependencyFile, dependency: Dependabot::Dependency).returns(T::Boolean) }
|
198
|
+
def requirement_changed?(file, dependency)
|
199
|
+
changed_requirements =
|
200
|
+
dependency.requirements - T.must(dependency.previous_requirements)
|
201
|
+
|
202
|
+
changed_requirements.any? { |f| f[:file] == file.name }
|
203
|
+
end
|
204
|
+
|
205
|
+
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T::Boolean) }
|
206
|
+
def specified_with_tag?(source)
|
207
|
+
!source[:tag].nil?
|
208
|
+
end
|
209
|
+
|
210
|
+
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T::Boolean) }
|
211
|
+
def specified_with_digest?(source)
|
212
|
+
!source[:digest].nil?
|
213
|
+
end
|
214
|
+
|
215
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, T.untyped]]) }
|
216
|
+
def requirements(file)
|
217
|
+
T.must(dependency).requirements
|
218
|
+
.select { |r| r[:file] == file.name }
|
219
|
+
end
|
220
|
+
|
221
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[T::Hash[Symbol, T.untyped]])) }
|
222
|
+
def previous_requirements(file)
|
223
|
+
T.must(dependency).previous_requirements
|
224
|
+
&.select { |r| r[:file] == file.name }
|
225
|
+
end
|
226
|
+
|
227
|
+
sig { params(source: T::Hash[Symbol, T.nilable(String)]).returns(T.nilable(String)) }
|
228
|
+
def private_registry_url(source)
|
229
|
+
source[:registry]
|
230
|
+
end
|
231
|
+
|
232
|
+
sig { params(file: Dependabot::DependencyFile).returns(T::Array[T::Hash[Symbol, T.nilable(String)]]) }
|
233
|
+
def sources(file)
|
234
|
+
requirements(file).map { |r| r.fetch(:source) }
|
235
|
+
end
|
236
|
+
|
237
|
+
sig { params(file: Dependabot::DependencyFile).returns(T.nilable(T::Array[T::Hash[Symbol, T.nilable(String)]])) }
|
238
|
+
def previous_sources(file)
|
239
|
+
previous_requirements(file)&.map { |r| r.fetch(:source) }
|
240
|
+
end
|
241
|
+
|
242
|
+
sig { returns(T.nilable(Dependabot::Dependency)) }
|
243
|
+
def dependency
|
244
|
+
# Files will only ever be updating a single dependency
|
245
|
+
dependencies.first
|
246
|
+
end
|
247
|
+
|
248
|
+
sig { override.void }
|
249
|
+
def check_required_files
|
250
|
+
return if dependency_files.any?
|
251
|
+
|
252
|
+
raise "No #{file_type}!"
|
253
|
+
end
|
254
|
+
|
255
|
+
private
|
256
|
+
|
257
|
+
sig { abstract.returns(String) }
|
258
|
+
def file_type; end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
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.297.
|
4
|
+
version: 0.297.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-19 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.297.
|
19
|
+
version: 0.297.1
|
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.297.
|
26
|
+
version: 0.297.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: debug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -251,15 +251,18 @@ files:
|
|
251
251
|
- lib/dependabot/docker/requirement.rb
|
252
252
|
- lib/dependabot/docker/tag.rb
|
253
253
|
- lib/dependabot/docker/update_checker.rb
|
254
|
-
- lib/dependabot/docker/utils/credentials_finder.rb
|
255
|
-
- lib/dependabot/docker/utils/helpers.rb
|
256
254
|
- lib/dependabot/docker/version.rb
|
255
|
+
- lib/dependabot/shared/shared_file_fetcher.rb
|
256
|
+
- lib/dependabot/shared/shared_file_parser.rb
|
257
|
+
- lib/dependabot/shared/shared_file_updater.rb
|
258
|
+
- lib/dependabot/shared/utils/credentials_finder.rb
|
259
|
+
- lib/dependabot/shared/utils/helpers.rb
|
257
260
|
homepage: https://github.com/dependabot/dependabot-core
|
258
261
|
licenses:
|
259
262
|
- MIT
|
260
263
|
metadata:
|
261
264
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
262
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.297.
|
265
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.297.1
|
263
266
|
post_install_message:
|
264
267
|
rdoc_options: []
|
265
268
|
require_paths:
|