Module: SemverDialects

Defined in:
lib/semver_dialects.rb,
lib/semver_dialects/apk.rb,
lib/semver_dialects/cli.rb,
lib/semver_dialects/rpm.rb,
lib/semver_dialects/maven.rb,
lib/semver_dialects/command.rb,
lib/semver_dialects/semver2.rb,
lib/semver_dialects/version.rb,
lib/semver_dialects/boundary.rb,
lib/semver_dialects/interval.rb,
lib/semver_dialects/base_version.rb,
lib/semver_dialects/interval_set.rb,
lib/semver_dialects/interval_parser.rb,
lib/semver_dialects/semantic_version.rb,
lib/semver_dialects/interval_set_parser.rb,
lib/semver_dialects/commands/check_version.rb,
lib/semver_dialects/commands/sort_versions.rb

Overview

IntervalSetParser parses a string that represents an interval set in a syntax that’s specific to a package type.

Defined Under Namespace

Modules: Apk, Commands, IntervalParser, IntervalSetParser, IntervalType, Maven, Rpm, Semver2 Classes: AboveAll, BaseVersion, BelowAll, Boundary, CLI, Command, EmptyInterval, Error, IncompleteScanError, Interval, IntervalSet, InvalidConstraintError, InvalidVersionError, SemanticVersion, SemanticVersionSegment, UnsupportedPackageTypeError, UnsupportedVersionError

Constant Summary collapse

VERSION =
'3.7.0'

Class Method Summary collapse

Class Method Details

.cleanup(raw_ver) ⇒ Object

cleanup loose npm versions



247
248
249
# File 'lib/semver_dialects.rb', line 247

def self.cleanup(raw_ver)
  raw_ver.strip.gsub(/^[=v]/, '')
end

.os_pkg_version_satisfies?(typ, raw_ver, raw_constraint) ⇒ Boolean

Returns:

  • (Boolean)

Raises:



105
106
107
108
109
110
111
112
113
114
115
# File 'lib/semver_dialects.rb', line 105

def self.os_pkg_version_satisfies?(typ, raw_ver, raw_constraint)
  return unless %w[deb rpm apk].include?(typ)
  # we only support the less than operator, because that's the only one currently output
  # by the advisory exporter for operating system packages.
  raise SemverDialects::InvalidConstraintError, raw_constraint unless raw_constraint[0] == '<'

  v1 = SemverDialects.parse_version(typ, raw_ver)
  v2 = SemverDialects.parse_version(typ, raw_constraint[1..])

  v1 < v2
end

.os_purl_type?(typ) ⇒ Boolean

Returns:

  • (Boolean)


101
102
103
# File 'lib/semver_dialects.rb', line 101

def self.os_purl_type?(typ)
  %w[deb rpm apk].include?(typ)
end

.parse_version(typ, raw_ver) ⇒ Object

Parse a version according to the syntax type.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/semver_dialects.rb', line 123

def self.parse_version(typ, raw_ver)
  # for efficiency most popular package types come first
  case typ
  when 'maven'
    Maven::VersionParser.parse(raw_ver)

  when 'npm'
    # npm follows Semver 2.0.0.
    Semver2::VersionParser.parse(cleanup(raw_ver))

  when 'go'
    # Go follows Semver 2.0.0.
    #
    # Go pseudo-versions are pre-releases as defined in Semver 2.0.0,
    # and can be compared as such. However, a pseudo-version can't be compared
    # to a pre-release or another pseudo-version of the same base version.
    #
    # quoting https://go.dev/ref/mod#pseudo-versions
    #
    # Each pseudo-version may be in one of three forms, depending on the base version.
    # These forms ensure that a pseudo-version compares higher than its base version,
    # but lower than the next tagged version.
    #

    # vX.0.0-yyyymmddhhmmss-abcdefabcdef is used when there is no known
    # base version. As with all versions, the major version X must match the
    # module’s major version suffix.
    #
    # vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef is used when the base version
    # is a pre-release version like vX.Y.Z-pre.
    #
    # vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef is used when the base version
    # is a release version like vX.Y.Z. For example, if the base version is
    # v1.2.3, a pseudo-version might be v1.2.4-0.20191109021931-daa7c04131f5.
    #
    Semver2::VersionParser.parse(raw_ver)

  when 'pypi'
    # See https://packaging.python.org/en/latest/specifications/version-specifiers/#version-specifiers
    # TODO: Implement a dedicated parser.
    SemanticVersion.new(raw_ver)

  when 'nuget'
    # NuGet diverges from Semver 2.0.0.
    #
    # quoting https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#where-nugetversion-diverges-from-semantic-versioning
    #
    # NuGetVersion supports a 4th version segment, Revision, to be compatible
    # with, or a superset of, System.Version. Therefore, excluding prerelease
    # and metadata labels, a version string is Major.Minor.Patch.Revision. As
    # per version normalization described above, if Revision is zero, it is
    # omitted from the normalized version string.
    #
    # NuGetVersion only requires the major segment to be defined. All others
    # are optional, and are equivalent to zero. This means that 1, 1.0,
    # 1.0.0, and 1.0.0.0 are all accepted and equal.
    #
    # NuGetVersion uses case insensitive string comparisons for pre-release
    # components. This means that 1.0.0-alpha and 1.0.0-Alpha are equal.
    #
    Semver2::VersionParser.parse(raw_ver.downcase)

  when 'gem'
    # Rubygem does not follow Semver. Its versioning scheme is not documented.
    #
    # quoting https://guides.rubygems.org/specification-reference/
    #
    # The version string can contain numbers and periods, such as 1.0.0. A
    # gem is a ‘prerelease’ gem if the version has a letter in it, such as
    # 1.0.0.pre.
    Gem::Version.new(raw_ver)

  when 'packagist'
    # Packagist defines specific identifiers like alpha, beta, and stable,
    # and the comparison rules for these are not compatible with Semver.
    # See https://github.com/composer/semver/blob/1d09200268e7d1052ded8e5da9c73c96a63d18f5/src/VersionParser.php#L39
    SemanticVersion.new(raw_ver)

  when 'conan'
    # Conan diverges from Semver 2.0.0.
    #
    # quoting https://docs.conan.io/2/tutorial/versioning/version_ranges.html#semantic-versioning
    #
    # Conan extends the semver specification to any number of digits, and
    # also allows to include lowercase letters in it. This was done because
    # during 1.X a lot of experience and feedback from users was gathered,
    # and it became evident than in C++ the versioning scheme is often more
    # complex, and users were demanding more flexibility, allowing versions
    # like 1.2.3.a.8 if necessary.
    #
    # Conan versions non-digit identifiers follow the same rules as package
    # names, they can only contain lowercase letters. This is to avoid
    # 1.2.3-Beta to be a different version than 1.2.3-beta which can be
    # problematic, even a security risk.
    #
    SemanticVersion.new(raw_ver)

  when 'cargo'
    # cargo follows Semver 2.0.0.
    Semver2::VersionParser.parse(raw_ver)

  when 'swift'
    # swift follows Semver 2.0.0.
    Semver2::VersionParser.parse(raw_ver)

  when 'pub'
    # pub follows Semver 2.0.0.
    Semver2::VersionParser.parse(raw_ver)

  when 'apk'
    Apk::VersionParser.parse(raw_ver)
  when 'deb'
    DebVersion.new(raw_ver)
  when 'rpm'
    Rpm::VersionParser.parse(raw_ver)
  else
    raise UnsupportedPackageTypeError, typ
  end
rescue ArgumentError
  # Gem::Version.new raises an ArgumentError for invalid versions.
  raise InvalidVersionError, raw_ver
end

.supported_package_typesObject

Returns array of supported package types



118
119
120
# File 'lib/semver_dialects.rb', line 118

def self.supported_package_types
  %w[maven npm go pypi nuget gem packagist conan cargo apk deb rpm swift]
end

.version_satisfies?(typ, raw_ver, raw_constraint) ⇒ Boolean

Determines if a version of a given package type satisfies a constraint.

On normal execution, this method might raise the following exceptions:

  • UnsupportedPackageTypeError if the package type is not supported

  • InvalidVersionError if the version is invalid

  • InvalidConstraintError if the constraint is invalid or contains invalid versions

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/semver_dialects.rb', line 88

def self.version_satisfies?(typ, raw_ver, raw_constraint)
  # os package versions are handled very differently from application package versions
  return os_pkg_version_satisfies?(typ, raw_ver, raw_constraint) if os_purl_type?(typ)

  # build an interval that only contains the version
  version = SemverDialects.parse_version(typ, raw_ver)
  version_as_interval = Interval.from_version(version)

  interval_set = IntervalSetParser.parse(typ, raw_constraint)

  interval_set.overlaps_with?(version_as_interval)
end