dependabot-docker 0.215.0 → 0.216.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70cbd954703bc927e710b32451ea52e68fdd7581df404a48055cd9de73329d26
4
- data.tar.gz: ed03cedf7c668151b6b29f98b1e75d33123d4e82e3483e08884ce3a18e16abd5
3
+ metadata.gz: f74b3c5969e731048741f4c9edd3d7d9043e029c6891473b9a22dbd03951c684
4
+ data.tar.gz: e180d84721d5c195361da30f1828bfd67d9b67075496ab220b6fb68f18b161f7
5
5
  SHA512:
6
- metadata.gz: acbc005bd461b204b732a76d901acc4d1aec7c18bebd9cfa8b6a6ee1e791a436f753c5ab81a02a1f797fbdce34048958d0dc2c64b9448690cf35ac269115b0e8
7
- data.tar.gz: 32d17f8cacae5c96729ce647f80897237829ee42f1268b6aa178e7b44608c5716b107cb385b1bec9b0dfdbc6a899ffe4b387abf242cb085c3bdbf73fa73e6621
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 = /@(?<digest>[^\s]+)/
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}?#{DIGEST}?#{NAME}?}x
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}?#{DIGEST}?#{NAME}?}x
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
- return parsed_from_line.fetch("tag") if parsed_from_line.fetch("tag")
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
- updated_content =
60
- if specified_with_digest?(file)
61
- update_digest_and_tag(file)
62
- else
63
- update_tag(file)
64
- end
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(file)
72
- old_declaration_regex = /^#{FROM_REGEX}\s+.*@#{old_digest(file)}/
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
- file.content.gsub(old_declaration_regex) do |old_dec|
87
+ previous_content.gsub(old_declaration_regex) do |old_dec|
75
88
  old_dec.
76
- gsub("@#{old_digest(file)}", "@#{new_digest(file)}").
77
- gsub(":#{dependency.previous_version}",
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(file)
83
- old_tags = old_tags(file)
84
- return if old_tags.empty?
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
- 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
-
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
- end
109
-
110
- modified_content
111
- end
103
+ old_declaration += "#{dependency.name}:#{old_tag}"
104
+ escaped_declaration = Regexp.escape(old_declaration)
112
105
 
113
- def specified_with_digest?(file)
114
- dependency.
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
- dependency.requirements.
124
- find { |r| r[:file] == file.name }.
125
- fetch(:source).fetch(:digest)
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 old_digest(file)
129
- return unless specified_with_digest?(file)
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
- dependency.requirements.
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
- dependency.
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(file)
150
- dependency.requirements.
151
- find { |r| r[:file] == file.name }.
152
- fetch(:source)[:registry]
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 = Utils.likely_helm_chart?(file) ? update_helm(file) : update_image(file)
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 = file.content
155
+ modified_content = content
169
156
 
170
157
  old_tags.each do |old_tag|
171
- old_tag_regex = /^\s+(?:-\s)?(?:tag|version):\s+#{old_tag}(?=\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 = file.content
170
+ modified_content = content
184
171
 
185
172
  old_images.each do |old_image|
186
- old_image_regex = /^\s+(?:-\s)?image:\s+#{old_image}(?=\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] ? "@#{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
- dependency.
209
- previous_requirements.
210
- select { |r| r[:file] == file.name }.map do |r|
211
- prefix = r.fetch(:source)[:registry] ? "#{r.fetch(:source)[:registry]}/" : ""
212
- digest = r.fetch(:source)[:digest] ? "@#{r.fetch(:source)[:digest]}" : ""
213
- tag = r.fetch(:source)[:tag] ? ":#{r.fetch(:source)[:tag]}" : ""
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
- dependency.
220
- previous_requirements.
221
- select { |r| r[:file] == file.name }.map do |r|
222
- r.fetch(:source)[:tag] || ""
223
- end
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
- # TODO: Find a way to add links to PRs
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
- updated_source[:digest] = updated_digest if req[:source][:digest]
74
- updated_source[:tag] = latest_version_from(req[:source][:tag]) if req[:source][:tag]
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
- !version_up_to_date?
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
- # If the tag isn't up-to-date then we can definitely update
97
- return false if version_tag_up_to_date?(dependency.version) == false
98
- return false if dependency.requirements.any? do |req|
99
- version_tag_up_to_date?(req.fetch(:source, {})[:tag]) == false
100
- end
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?(version)
108
- return unless version&.match?(NAME_WITH_VERSION)
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
- old_v = numeric_version_from(version)
113
- latest_v = numeric_version_from(latest_version)
81
+ return true unless version_tag.comparable?
114
82
 
115
- return true if version_class.new(latest_v) <= version_class.new(old_v)
83
+ latest_tag = latest_tag_from(version)
116
84
 
117
- # Check the precision of the potentially higher tag is the same as the
118
- # one it would replace. In the event that it's not the same, check the
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
- digest_of(version) == digest_of(latest_version)
88
+ version_class.new(latest_v) <= version_class.new(old_v)
123
89
  end
124
90
 
125
91
  def digest_up_to_date?
126
- dependency.requirements.all? do |req|
127
- next true unless req.fetch(:source)[:digest]
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) == new_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
- @versions ||= {}
136
- return @versions[version] if @versions.key?(version)
100
+ latest_tag_from(version).name
101
+ end
137
102
 
138
- @versions[version] = fetch_latest_version(version)
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 version (even if
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 fetch_latest_version(version)
144
- return version unless version.match?(NAME_WITH_VERSION)
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(version)
149
- candidate_tags = remove_version_downgrades(candidate_tags, version)
150
- candidate_tags = remove_prereleases(candidate_tags, version)
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, version)
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
- if latest_tag && same_precision?(latest_tag, version)
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
- if latest_same_precision_tag && digest_of(latest_same_precision_tag) == digest_of(latest_tag)
162
- latest_same_precision_tag
163
- else
164
- latest_tag || version
165
- end
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(version)
170
- original_prefix = prefix_of(version)
171
- original_suffix = suffix_of(version)
172
- original_format = format_of(version)
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 { |tag| tag.match?(NAME_WITH_VERSION) }.
177
- select { |tag| prefix_of(tag) == original_prefix }.
178
- select { |tag| format_of(tag) == original_format }
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| suffix_of(tag) == original_suffix }
165
+ candidate_tags.select { |tag| tag.suffix == original_suffix }
182
166
  end
183
167
 
184
- def remove_version_downgrades(candidate_tags, version)
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, version)
192
- return candidate_tags if prerelease?(version)
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, version)
182
+ def remove_precision_changes(candidate_tags, version_tag)
198
183
  candidate_tags.select do |tag|
199
- same_precision?(tag, version)
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 { |tag| canonical_version?(tag) }.
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 ||= digest_of(latest_version)
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
- begin
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
- retry
279
- end
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 numeric_version_from(tag).gsub(/kb/i, "").match?(/[a-zA-Z]/)
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 numeric_version_from(tag)
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(numeric_version_from(tag))
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, version)
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?(tag_a, version)
330
+ elsif tag_a.same_precision?(version_tag)
379
331
  1
380
- elsif same_precision?(tag_b, version)
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 && filter_lower_versions(filtered).empty? && filter_lower_versions(candidate_tags).any?
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
- versions_array = tags.map { |tag| comparable_version_from(tag) }
408
- versions_array.
409
- select { |version| version > comparable_version_from(dependency.version) }
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
- # If we don't have credentials, we might get them from the proxy
51
- return registry_details if registry_details["username"].nil?
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
- registry_hostname = registry_details.fetch("registry")
61
- region = registry_hostname.match(AWS_ECR_URL).
62
- named_captures.fetch("region")
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
- Aws::ECR::Client.new(region: region, credentials: aws_credentials).
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 < Gem::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 = Gem::Version.new(release_part.tr("-", "."))
17
+ @release_part = Dependabot::Version.new(release_part.tr("-", "."))
17
18
 
18
- @update_part = Gem::Version.new(update_part&.start_with?(/[0-9]/) ? update_part : 0)
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.215.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: 2022-12-07 00:00:00.000000000 Z
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.215.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.215.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.0.0
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.0.0
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.0.0
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.0.0
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.8'
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.8'
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.2'
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.2'
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.39.0
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.39.0
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.15.0
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.15.0
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.21.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.21.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.0
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.0
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.4'
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.4'
209
- description: Automated dependency management for Ruby, JavaScript, Python, PHP, Elixir,
210
- Rust, Java, .NET, Elm and Go
211
- email: support@dependabot.com
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.7
249
+ rubygems_version: 3.3.26
246
250
  signing_key:
247
251
  specification_version: 4
248
- summary: Docker support for dependabot-common
252
+ summary: Provides Dependabot support for Docker
249
253
  test_files: []