Module: StillActive::VersionHelper

Extended by:
VersionHelper
Included in:
VersionHelper
Defined in:
lib/helpers/version_helper.rb

Instance Method Summary collapse

Instance Method Details

#comparable(version_string) ⇒ Object

A Gem::Version for cross-ecosystem comparison (leading "v" and SemVer build metadata normalized away), or nil when the string can't be parsed. Public so the deps.dev client can rank a package's versions to find the real latest stable rather than trusting deps.dev's unreliable isDefault flag.



59
60
61
# File 'lib/helpers/version_helper.rb', line 59

def comparable(version_string)
  to_gem_version(version_string)
end

#find_version(versions:, version_string: nil, pre_release: false) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/helpers/version_helper.rb', line 9

def find_version(versions:, version_string: nil, pre_release: false)
  if version_string && pre_release
    versions&.find { |v| v["number"] == version_string && v["prerelease"] == pre_release }
  elsif !version_string.nil?
    versions&.find { |v| v["number"] == version_string }
  else
    # The "latest" of a kind: pick the highest by version rather than trust
    # the source's ordering. RubyGems happens to return newest-first, but
    # GitHub Packages and other sources don't, and a wrong "latest" cascades
    # into up_to_date and libyear.
    versions
      &.select { |v| v["prerelease"] == pre_release }
      &.max_by { |v| to_gem_version(v["number"]) || Gem::Version.new("0") }
  end
end

#gem_version(version_hash:) ⇒ Object



85
86
87
# File 'lib/helpers/version_helper.rb', line 85

def gem_version(version_hash:)
  version_hash&.dig("number")
end

#license(version_hash:) ⇒ Object

SPDX license identifier(s) from the RubyGems versions payload. Comma-joined when a gem declares more than one. nil when unknown.



106
107
108
109
110
111
# File 'lib/helpers/version_helper.rb', line 106

def license(version_hash:)
  licenses = version_hash&.dig("licenses")
  return if licenses.nil? || licenses.empty?

  licenses.join(", ")
end

#release_date(version_hash:) ⇒ Object



89
90
91
92
93
# File 'lib/helpers/version_helper.rb', line 89

def release_date(version_hash:)
  release_date = version_hash&.dig("created_at")

  Time.parse(release_date) unless release_date.nil?
end

#ruby_requirement(version_hash:) ⇒ Object

The version's declared Ruby requirement (the ruby_version field in the RubyGems versions payload, e.g. ">= 3.2", "< 3.2"), or nil when absent. This is the language-runtime ceiling's raw input; note it is ruby_version, not the gemspec's required_ruby_version.



99
100
101
102
# File 'lib/helpers/version_helper.rb', line 99

def ruby_requirement(version_hash:)
  requirement = version_hash&.dig("ruby_version")
  requirement unless requirement.nil? || requirement.empty?
end

#to_semver(version_string) ⇒ Object

Renders a MAJOR.MINOR.PATCH RubyGems version as SemVer 2.0.0, for fields that require it (SARIF's tool.driver.semanticVersion). A gem prerelease joins its prerelease identifiers to the release with a ".", SemVer with a "-": "3.0.0.rc4" -> "3.0.0-rc4", "1.2.3.pre.5" -> "1.2.3-pre.5". A plain release ("3.0.0") is already SemVer and passes through, and a short numeric release is padded to MAJOR.MINOR.PATCH. A string that doesn't start with a numeric segment is returned unchanged rather than coerced into a bogus version. Scoped to the tool's own version scheme: a 4+-segment numeric release (e.g. a gem like "6.1.7.6") is NOT converted to valid SemVer.



72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/helpers/version_helper.rb', line 72

def to_semver(version_string)
  return version_string if version_string.nil? || version_string.empty?

  parts = version_string.split(".")
  release = parts.take_while { |part| part.match?(/\A\d+\z/) }
  return version_string if release.empty?

  prerelease = parts.drop(release.length)
  release << "0" while release.length < 3
  base = release.join(".")
  prerelease.empty? ? base : "#{base}-#{prerelease.join(".")}"
end

#up_to_date(version_used:, latest_version: nil, latest_pre_release_version: nil) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/helpers/version_helper.rb', line 43

def up_to_date(version_used:, latest_version: nil, latest_pre_release_version: nil)
  return if latest_version.nil? && latest_pre_release_version.nil?

  used = to_gem_version(version_used)
  return if used.nil?

  [latest_version, latest_pre_release_version]
    .compact
    .filter_map { |v| to_gem_version(v) }
    .any? { |v| used >= v }
end

#upcoming_pre_release(pre_release:, release:) ⇒ Object

The latest pre-release is only a useful signal when it is newer than the latest stable release: an upcoming version you could opt into. A pre-release that predates the latest stable is historical noise (e.g. a lone 2009 rc on a gem now at 0.9.x, or an 8.1.0.rc1 after 8.1.2 already shipped), and it silently corrupts downstream logic: up_to_date compares used >= latest_pre_release, so any current version reads >= a stale pre-release and a behind gem is painted up to date (the futurist emoji). Returns the pre-release only when strictly newer than the release; keeps it when there is no stable release at all (a pre-release-only gem), where it is the only signal.



34
35
36
37
38
39
40
41
# File 'lib/helpers/version_helper.rb', line 34

def upcoming_pre_release(pre_release:, release:)
  return pre_release if release.nil?
  return if pre_release.nil?

  pre = to_gem_version(pre_release)
  stable = to_gem_version(release)
  pre_release if pre && stable && pre > stable
end