Class: Dependabot::Docker::Tag

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/dependabot/docker/tag.rb

Constant Summary collapse

WORDS_WITH_BUILD =
/(?:(?:-[a-z]+)+-[0-9]+)+/
VERSION_REGEX =
/v?(?<version>[0-9]+(?:[_.][0-9]+)*(?:\.[a-z0-9]+|#{WORDS_WITH_BUILD}|-(?:kb)?[0-9]+)*)/i
VERSION_WITH_SFX =
/^(?<operator>[~^<>=]*)#{VERSION_REGEX}(?<suffix>-[a-z][a-z0-9.\-]*)?$/i
VERSION_WITH_PFX =
/^(?<prefix>[a-z][a-z0-9.\-_]*-)?#{VERSION_REGEX}$/i
VERSION_WITH_PFX_AND_SFX =
/^(?<prefix>[a-z\-_]+-)?#{VERSION_REGEX}(?<suffix>-[a-z\-]+)?$/i
NAME_WITH_VERSION =
/
    #{VERSION_WITH_PFX}|
    #{VERSION_WITH_SFX}|
    #{VERSION_WITH_PFX_AND_SFX}
/x

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Tag

Returns a new instance of Tag.



27
28
29
# File 'lib/dependabot/docker/tag.rb', line 27

def initialize(name)
  @name = name
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



24
25
26
# File 'lib/dependabot/docker/tag.rb', line 24

def name
  @name
end

Instance Method Details

#canonical?Boolean

Returns:

  • (Boolean)


124
125
126
127
128
129
130
131
132
# File 'lib/dependabot/docker/tag.rb', line 124

def canonical?
  return false unless numeric_version
  return true if name == numeric_version

  # .NET tags are suffixed with -sdk
  return true if numeric_version && name == numeric_version.to_s + "-sdk"

  numeric_version && name == "jdk-" + T.must(numeric_version)
end

#comparable?Boolean

Returns:

  • (Boolean)


107
108
109
# File 'lib/dependabot/docker/tag.rb', line 107

def comparable?
  name.match?(NAME_WITH_VERSION)
end

#comparable_formats(other_format, other_prefix, other_suffix) ⇒ Object



82
83
84
85
86
87
# File 'lib/dependabot/docker/tag.rb', line 82

def comparable_formats(other_format, other_prefix, other_suffix)
  return false unless prefix.nil? && suffix.nil? && other_prefix.nil? && other_suffix.nil?

  formats = %i(year_month year_month_day)
  (format == :build_num && formats.include?(other_format)) || (formats.include?(format) && other_format == :build_num) # rubocop:disable Layout/LineLength
end

#comparable_to?(other) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/dependabot/docker/tag.rb', line 90

def comparable_to?(other)
  return false unless comparable?

  other_prefix = other.prefix
  other_suffix = other.suffix
  other_format = other.format

  equal_prefix = prefix == other_prefix
  equal_format = format == other_format
  comparable_format = comparable_formats(other.format, other.prefix, other.suffix)
  return equal_prefix && equal_format if other_format == :sha_suffixed

  equal_suffix = suffix == other_suffix
  (equal_prefix && equal_format && equal_suffix) || comparable_format
end

#digest?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'lib/dependabot/docker/tag.rb', line 37

def digest?
  name.match?(FileParser::DIGEST)
end

#formatObject



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/dependabot/docker/tag.rb', line 150

def format
  return :sha_suffixed if name.match?(/(^|\-g?)[0-9a-f]{7,}$/)
  return :year_month if version&.match?(/^[12]\d{3}(?:[.\-]|$)/)
  return :year_month_day if version&.match?(/^[12](?:\d{5}|\d{7})(?:[.\-]|$)/)
  return :build_num if version&.match?(/^\d+$/)

  # As an example, "21-ea-32", "22-ea-7", and "22-ea-jdk-nanoserver-1809"
  # are mapped to "<version>-ea-<build_num>", "<version>-ea-<build_num>",
  # and "<version>-ea-jdk-nanoserver-<build_num>" respectively.
  #
  # That means only "22-ea-7" will be considered as a viable update
  # candidate for "21-ea-32", since it's the only one that respects that
  # format.
  if version&.match?(WORDS_WITH_BUILD)
    return :"<version>#{T.must(version).match(WORDS_WITH_BUILD).to_s.gsub(/-[0-9]+/, '-<build_num>')}"
  end

  :normal
end

#looks_like_prerelease?Boolean

Returns:

  • (Boolean)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/dependabot/docker/tag.rb', line 42

def looks_like_prerelease?
  return false unless comparable?

  # Don't treat SHA-suffixed tags as prereleases (e.g., v3.10.0-169-gfe040d3)
  return false if format == :sha_suffixed

  # Check for common prerelease patterns in the tag name
  # The version regex splits things like "1.0.0-alpha" into version="1.0.0" and suffix="-alpha"
  # So we need to check the full name or the combination of version and suffix
  prerelease_patterns = [
    /alpha/i,       # matches: alpha, ALPHA
    /beta/i,        # matches: beta, BETA
    /rc\d*/i,       # matches: rc, RC, RC1, rc2, etc.
    /dev/i,         # matches: dev, DEV
    /preview/i,     # matches: preview, PREVIEW
    /\bpre\b/i,     # matches: pre, PRE as a whole word
    /nightly/i,     # matches: nightly, NIGHTLY
    /snapshot/i,    # matches: snapshot, SNAPSHOT
    /canary/i,      # matches: canary, CANARY
    /unstable/i,    # matches: unstable, UNSTABLE
    /\d+[a-z]\d*/,  # matches: 3.15.0a2, 1.0b1 (version followed by letter and optional number)
    /[a-z]+\d+$/,   # matches: alpha1, beta2, rc3 at the end
    /\.post\d+/i,   # matches: .post1, .POST2 (Python PEP 440 post-release)
    /\.dev\d+/i     # matches: .dev0, .DEV1 (Python PEP 440 development release)
  ]

  # Check both the version part and the suffix part
  version_matches = version && prerelease_patterns.any? { |pattern| T.must(version).match?(pattern) }
  suffix_matches = suffix && prerelease_patterns.any? { |pattern| T.must(suffix).match?(pattern) }

  !!(version_matches || suffix_matches)
end

#numeric_versionObject



171
172
173
174
175
# File 'lib/dependabot/docker/tag.rb', line 171

def numeric_version
  return unless comparable?

  version&.gsub(/kb/i, "")&.gsub(/-[a-z]+/, "")&.downcase
end

#precisionObject



178
179
180
# File 'lib/dependabot/docker/tag.rb', line 178

def precision
  segments.length
end

#prefixObject



135
136
137
# File 'lib/dependabot/docker/tag.rb', line 135

def prefix
  name.match(NAME_WITH_VERSION)&.named_captures&.fetch("prefix")
end

#same_but_less_precise?(other) ⇒ Boolean

Returns:

  • (Boolean)


117
118
119
120
121
# File 'lib/dependabot/docker/tag.rb', line 117

def same_but_less_precise?(other)
  other.segments.zip(segments).all? do |segment, other_segment|
    segment == other_segment || other_segment.nil?
  end
end

#same_precision?(other) ⇒ Boolean

Returns:

  • (Boolean)


112
113
114
# File 'lib/dependabot/docker/tag.rb', line 112

def same_precision?(other)
  other.precision == precision
end

#segmentsObject



183
184
185
# File 'lib/dependabot/docker/tag.rb', line 183

def segments
  T.must(numeric_version).split(/[.-]/)
end

#suffixObject



140
141
142
# File 'lib/dependabot/docker/tag.rb', line 140

def suffix
  name.match(NAME_WITH_VERSION)&.named_captures&.fetch("suffix")
end

#to_sObject



32
33
34
# File 'lib/dependabot/docker/tag.rb', line 32

def to_s
  name
end

#versionObject



145
146
147
# File 'lib/dependabot/docker/tag.rb', line 145

def version
  name.match(NAME_WITH_VERSION)&.named_captures&.fetch("version")
end