dependabot-docker_compose 0.297.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/dependabot/docker_compose/file_fetcher.rb +67 -0
- data/lib/dependabot/docker_compose/file_parser.rb +76 -0
- data/lib/dependabot/docker_compose/file_updater.rb +106 -0
- data/lib/dependabot/docker_compose/metadata_finder.rb +39 -0
- data/lib/dependabot/docker_compose/package_manager.rb +53 -0
- data/lib/dependabot/docker_compose/requirement.rb +43 -0
- data/lib/dependabot/docker_compose/tag.rb +144 -0
- data/lib/dependabot/docker_compose/update_checker.rb +479 -0
- data/lib/dependabot/docker_compose/version.rb +84 -0
- data/lib/dependabot/docker_compose.rb +19 -0
- 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/shared/utils/credentials_finder.rb +101 -0
- data/lib/dependabot/shared/utils/helpers.rb +19 -0
- metadata +285 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e8800055af3f3a9fc1a7b056288ea510770fd65dcf8933eb69a66a4aed591914
|
4
|
+
data.tar.gz: 05c25842d1a91b7fc0cddff8e51b0687d5ff0f1cb200276409e25671cf245f46
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c8f586a6ee7a26cb2e0e92abca5e23a0304537236df1fac702a667dfb8117494ce16d3d3243e20b4003e181c129e998381c7e5cca72da98ffaab7f2ff5aaf9b1
|
7
|
+
data.tar.gz: 72d33eae6923a14d9ffc8f12ab9cfdf0cddab97ce91afa51a39be2bf8fa618bbb678cdc6408166f54745d3c5305800be91d5328c6c2a9eef8d421cfc7664499e
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/shared/shared_file_fetcher"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module DockerCompose
|
8
|
+
class FileFetcher < Dependabot::Shared::SharedFileFetcher
|
9
|
+
FILENAME_REGEX = /(docker-)?compose(-[\w]+)?(?>\.[\w-]+)?\.ya?ml/i
|
10
|
+
|
11
|
+
sig { override.returns(T::Array[DependencyFile]) }
|
12
|
+
def fetch_files
|
13
|
+
fetched_files = []
|
14
|
+
fetched_files += correctly_encoded_docker_compose_files if allow_beta_ecosystems?
|
15
|
+
|
16
|
+
return fetched_files if fetched_files.any?
|
17
|
+
|
18
|
+
raise_appropriate_error
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { override.returns(Regexp) }
|
22
|
+
def self.filename_regex
|
23
|
+
FILENAME_REGEX
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
27
|
+
def docker_compose_files
|
28
|
+
@docker_compose_files ||=
|
29
|
+
T.let(repo_contents(raise_errors: false)
|
30
|
+
.select { |f| f.type == "file" && f.name.match?(FILENAME_REGEX) }
|
31
|
+
.map { |f| fetch_file_from_host(f.name) }, T.nilable(T::Array[DependencyFile]))
|
32
|
+
end
|
33
|
+
|
34
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
35
|
+
def correctly_encoded_docker_compose_files
|
36
|
+
docker_compose_files.select { |f| T.must(f.content).valid_encoding? }
|
37
|
+
end
|
38
|
+
|
39
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
40
|
+
def incorrectly_encoded_docker_compose_files
|
41
|
+
docker_compose_files.reject { |f| T.must(f.content).valid_encoding? }
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { override.returns(String) }
|
45
|
+
def self.required_files_message
|
46
|
+
"Repo must contain a docker-compose.yaml file."
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
sig { override.returns(String) }
|
52
|
+
def default_file_name
|
53
|
+
"docker-compose.yml"
|
54
|
+
end
|
55
|
+
|
56
|
+
sig { override.returns(String) }
|
57
|
+
def file_type
|
58
|
+
"Docker Compose"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Dependabot::FileFetchers.register(
|
65
|
+
"docker_compose",
|
66
|
+
Dependabot::DockerCompose::FileFetcher
|
67
|
+
)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "yaml"
|
5
|
+
require "dependabot/shared/shared_file_parser"
|
6
|
+
require "dependabot/docker_compose/package_manager"
|
7
|
+
|
8
|
+
module Dependabot
|
9
|
+
module DockerCompose
|
10
|
+
class FileParser < Dependabot::Shared::SharedFileParser
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
FROM_IMAGE = %r{^(?:#{REGISTRY}/)?#{IMAGE}(?:#{TAG})?(?:#{DIGEST})?(?:#{NAME})?}
|
14
|
+
|
15
|
+
sig { returns(Ecosystem) }
|
16
|
+
def ecosystem
|
17
|
+
@ecosystem ||= T.let(
|
18
|
+
Ecosystem.new(
|
19
|
+
name: ECOSYSTEM,
|
20
|
+
package_manager: DockerPackageManager.new
|
21
|
+
),
|
22
|
+
T.nilable(Ecosystem)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.returns(T::Array[Dependabot::Dependency]) }
|
27
|
+
def parse
|
28
|
+
dependency_set = DependencySet.new
|
29
|
+
|
30
|
+
composefiles.each do |composefile|
|
31
|
+
yaml = YAML.safe_load(T.must(composefile.content))
|
32
|
+
yaml["services"].each do |_, service|
|
33
|
+
parsed_from_image = T.must(FROM_IMAGE.match(service["image"])).named_captures
|
34
|
+
parsed_from_image["registry"] = nil if parsed_from_image["registry"] == "docker.io"
|
35
|
+
|
36
|
+
version = version_from(parsed_from_image)
|
37
|
+
next unless version
|
38
|
+
|
39
|
+
dependency_set << build_dependency(composefile, parsed_from_image, version)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
dependency_set.dependencies
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
sig { override.returns(String) }
|
49
|
+
def package_manager
|
50
|
+
"docker_compose"
|
51
|
+
end
|
52
|
+
|
53
|
+
sig { override.returns(String) }
|
54
|
+
def file_type
|
55
|
+
"docker-compose.yml"
|
56
|
+
end
|
57
|
+
|
58
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
59
|
+
def composefiles
|
60
|
+
dependency_files
|
61
|
+
end
|
62
|
+
|
63
|
+
sig { override.void }
|
64
|
+
def check_required_files
|
65
|
+
return if dependency_files.any?
|
66
|
+
|
67
|
+
raise "No #{file_type}!"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Dependabot::FileParsers.register(
|
74
|
+
"docker_compose",
|
75
|
+
Dependabot::DockerCompose::FileParser
|
76
|
+
)
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/shared/shared_file_updater"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module DockerCompose
|
8
|
+
class FileUpdater < Dependabot::Shared::SharedFileUpdater
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Helpers
|
11
|
+
|
12
|
+
YAML_REGEXP = /(docker-)?compose(?>\.[\w-]+)?\.ya?ml/i
|
13
|
+
IMAGE_REGEX = /image:\s*/
|
14
|
+
|
15
|
+
sig { override.returns(T::Array[Regexp]) }
|
16
|
+
def self.updated_files_regex
|
17
|
+
[YAML_REGEXP]
|
18
|
+
end
|
19
|
+
|
20
|
+
sig { override.returns(String) }
|
21
|
+
def file_type
|
22
|
+
"Docker compose"
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { override.returns(Regexp) }
|
26
|
+
def yaml_file_pattern
|
27
|
+
YAML_REGEXP
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { override.returns(Regexp) }
|
31
|
+
def container_image_regex
|
32
|
+
IMAGE_REGEX
|
33
|
+
end
|
34
|
+
|
35
|
+
sig { override.params(escaped_declaration: String).returns(Regexp) }
|
36
|
+
def build_old_declaration_regex(escaped_declaration)
|
37
|
+
%r{#{IMAGE_REGEX}\s+(docker\.io/)?#{escaped_declaration}(?=\s|$)}
|
38
|
+
end
|
39
|
+
|
40
|
+
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
41
|
+
def updated_dependency_files
|
42
|
+
updated_files = []
|
43
|
+
dependency_files.each do |file|
|
44
|
+
next unless requirement_changed?(file, T.must(dependency))
|
45
|
+
|
46
|
+
updated_files << updated_file(
|
47
|
+
file: file,
|
48
|
+
content: T.must(updated_dockerfile_content(file))
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
updated_files.reject! { |f| dependency_files.include?(f) }
|
53
|
+
raise "No files changed!" if updated_files.none?
|
54
|
+
|
55
|
+
updated_files
|
56
|
+
end
|
57
|
+
|
58
|
+
sig do
|
59
|
+
override.params(previous_content: String, old_source: T::Hash[Symbol, T.nilable(String)],
|
60
|
+
new_source: T::Hash[Symbol, T.nilable(String)]).returns(String)
|
61
|
+
end
|
62
|
+
def update_digest_and_tag(previous_content, old_source, new_source)
|
63
|
+
old_digest = old_source[:digest]
|
64
|
+
new_digest = new_source[:digest]
|
65
|
+
|
66
|
+
old_tag = old_source[:tag]
|
67
|
+
new_tag = new_source[:tag]
|
68
|
+
|
69
|
+
old_declaration =
|
70
|
+
if private_registry_url(old_source)
|
71
|
+
"#{private_registry_url(old_source)}/"
|
72
|
+
else
|
73
|
+
""
|
74
|
+
end
|
75
|
+
old_declaration += T.must(dependency).name
|
76
|
+
old_declaration +=
|
77
|
+
if specified_with_tag?(old_source)
|
78
|
+
":#{old_tag}"
|
79
|
+
else
|
80
|
+
""
|
81
|
+
end
|
82
|
+
old_declaration +=
|
83
|
+
if old_digest&.start_with?("sha256:")
|
84
|
+
"@#{old_digest}"
|
85
|
+
else
|
86
|
+
""
|
87
|
+
end
|
88
|
+
|
89
|
+
escaped_declaration = Regexp.escape(old_declaration)
|
90
|
+
|
91
|
+
old_declaration_regex = build_old_declaration_regex(escaped_declaration)
|
92
|
+
|
93
|
+
previous_content.gsub(old_declaration_regex) do |old_dec|
|
94
|
+
old_dec
|
95
|
+
.gsub(":#{old_tag}", ":#{new_tag}")
|
96
|
+
.gsub(old_digest.to_s, new_digest.to_s)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
Dependabot::FileUpdaters.register(
|
104
|
+
"docker_compose",
|
105
|
+
Dependabot::DockerCompose::FileUpdater
|
106
|
+
)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dependabot/metadata_finders"
|
5
|
+
require "dependabot/metadata_finders/base"
|
6
|
+
require "dependabot/shared_helpers"
|
7
|
+
require "sorbet-runtime"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module DockerCompose
|
11
|
+
class MetadataFinder < Dependabot::MetadataFinders::Base
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
sig { override.returns(T.nilable(Dependabot::Source)) }
|
17
|
+
def look_up_source
|
18
|
+
return if dependency.requirements.empty?
|
19
|
+
|
20
|
+
new_source = dependency.requirements.first&.fetch(:source)
|
21
|
+
return unless new_source && new_source[:registry] && new_source[:tag]
|
22
|
+
|
23
|
+
image_ref = "#{new_source[:registry]}/#{dependency.name}:#{new_source[:tag]}"
|
24
|
+
image_details_output = SharedHelpers.run_shell_command("regctl image inspect #{image_ref}")
|
25
|
+
image_details = JSON.parse(image_details_output)
|
26
|
+
image_source = image_details.dig("config", "Labels", "org.opencontainers.image.source")
|
27
|
+
return unless image_source
|
28
|
+
|
29
|
+
Dependabot::Source.from_url(image_source)
|
30
|
+
rescue StandardError => e
|
31
|
+
Dependabot.logger.warn("Error looking up Docker Compose source: #{e.message}")
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Dependabot::MetadataFinders
|
39
|
+
.register("docker_compose", Dependabot::DockerCompose::MetadataFinder)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
require "dependabot/docker_compose/version"
|
6
|
+
require "dependabot/ecosystem"
|
7
|
+
require "dependabot/docker_compose/requirement"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module DockerCompose
|
11
|
+
ECOSYSTEM = "docker_compose"
|
12
|
+
|
13
|
+
SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
14
|
+
|
15
|
+
DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
16
|
+
|
17
|
+
class DockerPackageManager < Dependabot::Ecosystem::VersionManager
|
18
|
+
extend T::Sig
|
19
|
+
|
20
|
+
NAME = "docker_compose"
|
21
|
+
|
22
|
+
# As docker_compose updater is an in house custom utility, We use a placeholder
|
23
|
+
# version number for docker_compose updater
|
24
|
+
VERSION = "1.0.0"
|
25
|
+
|
26
|
+
SUPPORTED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
27
|
+
|
28
|
+
DEPRECATED_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
29
|
+
|
30
|
+
sig do
|
31
|
+
void
|
32
|
+
end
|
33
|
+
def initialize
|
34
|
+
super(
|
35
|
+
name: NAME,
|
36
|
+
version: Version.new(VERSION),
|
37
|
+
deprecated_versions: DEPRECATED_VERSIONS,
|
38
|
+
supported_versions: SUPPORTED_VERSIONS
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
sig { override.returns(T::Boolean) }
|
43
|
+
def deprecated?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
sig { override.returns(T::Boolean) }
|
48
|
+
def unsupported?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
require "dependabot/requirement"
|
7
|
+
require "dependabot/utils"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module DockerCompose
|
11
|
+
# Lifted from the bundler package manager
|
12
|
+
class Requirement < Dependabot::Requirement
|
13
|
+
extend T::Sig
|
14
|
+
|
15
|
+
# For consistency with other languages, we define a requirements array.
|
16
|
+
# Ruby doesn't have an `OR` separator for requirements, so it always
|
17
|
+
# contains a single element.
|
18
|
+
sig { override.params(requirement_string: T.nilable(String)).returns(T::Array[Requirement]) }
|
19
|
+
def self.requirements_array(requirement_string)
|
20
|
+
[new(T.must(requirement_string))]
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { override.params(version: Version).returns(T::Boolean) }
|
24
|
+
def satisfied_by?(version)
|
25
|
+
super(version.release_part)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Patches Gem::Requirement to make it accept requirement strings like
|
29
|
+
# "~> 4.2.5, >= 4.2.5.1" without first needing to split them.
|
30
|
+
sig { params(requirements: T.any(T.nilable(String), T::Array[T.nilable(String)])).void }
|
31
|
+
def initialize(*requirements)
|
32
|
+
requirements = requirements.flatten.flat_map do |req_string|
|
33
|
+
req_string.to_s.split(",").map(&:strip)
|
34
|
+
end
|
35
|
+
|
36
|
+
super(requirements)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
Dependabot::Utils
|
43
|
+
.register_requirement_class("docker_compose", Dependabot::DockerCompose::Requirement)
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# typed: strong
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
|
6
|
+
module Dependabot
|
7
|
+
module DockerCompose
|
8
|
+
class Tag
|
9
|
+
extend T::Sig
|
10
|
+
WORDS_WITH_BUILD = /(?:(?:-[a-z]+)+-[0-9]+)+/
|
11
|
+
VERSION_REGEX = /v?(?<version>[0-9]+(?:[_.][0-9]+)*(?:\.[a-z0-9]+|#{WORDS_WITH_BUILD}|-(?:kb)?[0-9]+)*)/i
|
12
|
+
VERSION_WITH_SFX = /^#{VERSION_REGEX}(?<suffix>-[a-z][a-z0-9.\-]*)?$/i
|
13
|
+
VERSION_WITH_PFX = /^(?<prefix>[a-z][a-z0-9.\-_]*-)?#{VERSION_REGEX}$/i
|
14
|
+
VERSION_WITH_PFX_AND_SFX = /^(?<prefix>[a-z\-_]+-)?#{VERSION_REGEX}(?<suffix>-[a-z\-]+)?$/i
|
15
|
+
NAME_WITH_VERSION =
|
16
|
+
/
|
17
|
+
#{VERSION_WITH_PFX}|
|
18
|
+
#{VERSION_WITH_SFX}|
|
19
|
+
#{VERSION_WITH_PFX_AND_SFX}
|
20
|
+
/x
|
21
|
+
DIGEST = /@(?<digest>[^\s]+)/
|
22
|
+
|
23
|
+
sig { returns(String) }
|
24
|
+
attr_reader :name
|
25
|
+
|
26
|
+
sig { params(name: String).void }
|
27
|
+
def initialize(name)
|
28
|
+
@name = name
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { returns(String) }
|
32
|
+
def to_s
|
33
|
+
name
|
34
|
+
end
|
35
|
+
|
36
|
+
sig { returns(T::Boolean) }
|
37
|
+
def digest?
|
38
|
+
name.match?(DIGEST)
|
39
|
+
end
|
40
|
+
|
41
|
+
sig { returns(T.nilable(T::Boolean)) }
|
42
|
+
def looks_like_prerelease?
|
43
|
+
numeric_version&.match?(/[a-zA-Z]/)
|
44
|
+
end
|
45
|
+
|
46
|
+
sig { params(other: Tag).returns(T::Boolean) }
|
47
|
+
def comparable_to?(other)
|
48
|
+
return false unless comparable?
|
49
|
+
|
50
|
+
other_prefix = other.prefix
|
51
|
+
other_suffix = other.suffix
|
52
|
+
other_format = other.format
|
53
|
+
|
54
|
+
equal_prefix = prefix == other_prefix
|
55
|
+
equal_format = format == other_format
|
56
|
+
return equal_prefix && equal_format if other_format == :sha_suffixed
|
57
|
+
|
58
|
+
equal_suffix = suffix == other_suffix
|
59
|
+
equal_prefix && equal_format && equal_suffix
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { returns(T::Boolean) }
|
63
|
+
def comparable?
|
64
|
+
name.match?(NAME_WITH_VERSION)
|
65
|
+
end
|
66
|
+
|
67
|
+
sig { params(other: Tag).returns(T::Boolean) }
|
68
|
+
def same_precision?(other)
|
69
|
+
other.precision == precision
|
70
|
+
end
|
71
|
+
|
72
|
+
sig { params(other: Tag).returns(T::Boolean) }
|
73
|
+
def same_but_less_precise?(other)
|
74
|
+
other.segments.zip(segments).all? do |segment, other_segment|
|
75
|
+
segment == other_segment || other_segment.nil?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
sig { returns(T.nilable(T::Boolean)) }
|
80
|
+
def canonical?
|
81
|
+
return false unless numeric_version
|
82
|
+
return true if name == numeric_version
|
83
|
+
|
84
|
+
# .NET tags are suffixed with -sdk
|
85
|
+
return true if numeric_version && name == numeric_version.to_s + "-sdk"
|
86
|
+
|
87
|
+
numeric_version && name == "jdk-" + T.must(numeric_version)
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { returns T.nilable(String) }
|
91
|
+
def prefix
|
92
|
+
name.match(NAME_WITH_VERSION)&.named_captures&.fetch("prefix")
|
93
|
+
end
|
94
|
+
|
95
|
+
sig { returns T.nilable(String) }
|
96
|
+
def suffix
|
97
|
+
name.match(NAME_WITH_VERSION)&.named_captures&.fetch("suffix")
|
98
|
+
end
|
99
|
+
|
100
|
+
sig { returns T.nilable(String) }
|
101
|
+
def version
|
102
|
+
name.match(NAME_WITH_VERSION)&.named_captures&.fetch("version")
|
103
|
+
end
|
104
|
+
|
105
|
+
sig { returns(Symbol) }
|
106
|
+
def format
|
107
|
+
return :sha_suffixed if name.match?(/(^|\-g?)[0-9a-f]{7,}$/)
|
108
|
+
return :year_month if version&.match?(/^[12]\d{3}(?:[.\-]|$)/)
|
109
|
+
return :year_month_day if version&.match?(/^[12](?:\d{5}|\d{7})(?:[.\-]|$)/)
|
110
|
+
return :build_num if version&.match?(/^\d+$/)
|
111
|
+
|
112
|
+
# As an example, "21-ea-32", "22-ea-7", and "22-ea-jdk-nanoserver-1809"
|
113
|
+
# are mapped to "<version>-ea-<build_num>", "<version>-ea-<build_num>",
|
114
|
+
# and "<version>-ea-jdk-nanoserver-<build_num>" respectively.
|
115
|
+
#
|
116
|
+
# That means only "22-ea-7" will be considered as a viable update
|
117
|
+
# candidate for "21-ea-32", since it's the only one that respects that
|
118
|
+
# format.
|
119
|
+
if version&.match?(WORDS_WITH_BUILD)
|
120
|
+
return :"<version>#{T.must(version).match(WORDS_WITH_BUILD).to_s.gsub(/-[0-9]+/, '-<build_num>')}"
|
121
|
+
end
|
122
|
+
|
123
|
+
:normal
|
124
|
+
end
|
125
|
+
|
126
|
+
sig { returns(T.nilable(String)) }
|
127
|
+
def numeric_version
|
128
|
+
return unless comparable?
|
129
|
+
|
130
|
+
version&.gsub(/kb/i, "")&.gsub(/-[a-z]+/, "")&.downcase
|
131
|
+
end
|
132
|
+
|
133
|
+
sig { returns(Integer) }
|
134
|
+
def precision
|
135
|
+
segments.length
|
136
|
+
end
|
137
|
+
|
138
|
+
sig { returns(T::Array[String]) }
|
139
|
+
def segments
|
140
|
+
T.must(numeric_version).split(/[.-]/)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|