Class: Ukiryu::Definition::VersionResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/ukiryu/definition/version_resolver.rb

Overview

Resolve semantic version constraints

This class handles semantic versioning constraints like:

  • “1.0” - exact version

  • “>= 1.0” - minimum version (inclusive)

  • “~> 1.2” - pessimistic version constraint

Defined Under Namespace

Classes: Constraint

Class Method Summary collapse

Class Method Details

.compare_versions(v1, v2) ⇒ Integer

Compare two version strings

Parameters:

  • v1 (String, Array)

    first version or parts

  • v2 (String, Array)

    second version or parts

Returns:

  • (Integer)

    comparison result (-1, 0, 1)



149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ukiryu/definition/version_resolver.rb', line 149

def self.compare_versions(v1, v2)
  parts1 = v1.is_a?(Array) ? v1 : parse_version(v1)
  parts2 = v2.is_a?(Array) ? v2 : parse_version(v2)

  max_length = [parts1.length, parts2.length].max
  max_length.times do |i|
    p1 = parts1[i] || 0
    p2 = parts2[i] || 0
    comparison = p1 <=> p2
    return comparison unless comparison.zero?
  end

  0
end

.latest(versions) ⇒ String?

Get the latest version from a list

Parameters:

  • versions (Array<String>)

    list of versions

Returns:

  • (String, nil)

    the latest version



176
177
178
179
180
# File 'lib/ukiryu/definition/version_resolver.rb', line 176

def self.latest(versions)
  return nil if versions.nil? || versions.empty?

  versions.max_by { |v| parse_version(v) }
end

.parse_constraint(constraint_string) ⇒ Array<Constraint>

Parse a version constraint string

Parameters:

  • constraint_string (String)

    the constraint string

Returns:



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/ukiryu/definition/version_resolver.rb', line 57

def self.parse_constraint(constraint_string)
  return [Constraint.exact(constraint_string)] unless constraint_string.match?(/[<>=~]/)

  constraints = []

  # Split by comma for compound constraints
  constraint_string.split(',').map(&:strip).each do |part|
    case part
    when /\A~>(?:\s*(.+))?/ # Updated regex for ~> operator
      # Pessimistic version constraint
      version = ::Regexp.last_match(1) || ''
      constraints << Constraint.pessimistic(version.strip)
    when /\A>=\s*(.+)/
      # Minimum version (inclusive)
      constraints << Constraint.min(::Regexp.last_match(1))
    when /\A>\s*(.+)/
      # Minimum version (exclusive)
      constraints << Constraint.new(:>, ::Regexp.last_match(1))
    when /\A<=\s*(.+)/
      # Maximum version (inclusive)
      constraints << Constraint.max(::Regexp.last_match(1))
    when /\A<\s*(.+)/
      # Maximum version (exclusive)
      constraints << Constraint.new(:<, ::Regexp.last_match(1))
    when /\A==\s*(.+)/
      # Exact version
      constraints << Constraint.exact(::Regexp.last_match(1))
    else
      # Assume exact version
      constraints << Constraint.exact(part.strip)
    end
  end

  constraints
end

.parse_version(version_string) ⇒ Array<Integer>

Parse a version string into components

Parameters:

  • version_string (String)

    the version string

Returns:

  • (Array<Integer>)

    version components



168
169
170
# File 'lib/ukiryu/definition/version_resolver.rb', line 168

def self.parse_version(version_string)
  version_string.to_s.split('.').map(&:to_i)
end

.pessimistic_range(version) ⇒ Array<String>

Get the compatible version range for a pessimistic constraint

Parameters:

  • version (String)

    the base version

Returns:

  • (Array<String>)
    min_version, max_version


186
187
188
189
190
191
# File 'lib/ukiryu/definition/version_resolver.rb', line 186

def self.pessimistic_range(version)
  parts = parse_version(version)
  min = version
  max = "#{parts[0...-1].join('.')}.#{parts[-1] + 1}"
  [min, max]
end

.resolve(constraint, available_versions) ⇒ String?

Resolve the best matching version from available versions

Parameters:

  • constraint (String)

    the version constraint

  • available_versions (Array<String>)

    available versions

Returns:

  • (String, nil)

    best matching version, or nil if none match



131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/ukiryu/definition/version_resolver.rb', line 131

def self.resolve(constraint, available_versions)
  return nil if available_versions.nil? || available_versions.empty?

  # Parse constraint
  constraints = parse_constraint(constraint)

  # Filter versions that satisfy the constraint
  matching = available_versions.select { |v| satisfies?(v, constraints) }

  # Return highest matching version
  matching.max_by { |v| parse_version(v) }
end

.satisfies?(version, constraint) ⇒ Boolean

Check if a version satisfies a constraint

Parameters:

  • version (String)

    the version to check

  • constraint (String, Array<Constraint>)

    the constraint(s)

Returns:

  • (Boolean)

    true if version satisfies constraint



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/ukiryu/definition/version_resolver.rb', line 98

def self.satisfies?(version, constraint)
  constraints = constraint.is_a?(Array) ? constraint : parse_constraint(constraint)

  v_parts = parse_version(version)

  constraints.all? do |c|
    case c.operator
    when :==
      v_parts == parse_version(c.version)
    when :>=
      compare_versions(v_parts, parse_version(c.version)) >= 0
    when :>
      compare_versions(v_parts, parse_version(c.version)).positive?
    when :<=
      compare_versions(v_parts, parse_version(c.version)) <= 0
    when :<
      compare_versions(v_parts, parse_version(c.version)).negative?
    when '~>'.to_sym
      # Pessimistic version constraint: >= x.y.z, < x.(y+1).0
      base = parse_version(c.version)
      upper = base[0...-1] + [base[-1] + 1, 0]
      compare_versions(v_parts, base) >= 0 && compare_versions(v_parts, upper).negative?
    else
      false
    end
  end
end