dependabot-docker 0.215.0 → 0.216.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml 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: []