Module: Kettle::Dev::Versioning

Defined in:
lib/kettle/dev/versioning.rb

Overview

Shared helpers for version detection and bump classification.

Class Method Summary collapse

Class Method Details

.abort!(msg) ⇒ void

This method returns an undefined value.

Abort via ExitAdapter if available; otherwise Kernel.abort

Parameters:

  • msg (String)


97
98
99
# File 'lib/kettle/dev/versioning.rb', line 97

def abort!(msg)
  Kettle::Dev::ExitAdapter.abort(msg)
end

.classify_bump(prev, cur) ⇒ Symbol

Classify the bump type from prev -> cur. EPIC is a MAJOR > 1000.

Parameters:

  • prev (String)

    previous released version

  • cur (String)

    current version (from version.rb)

Returns:

  • (Symbol)

    one of :epic, :major, :minor, :patch, :same, :downgrade



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/kettle/dev/versioning.rb', line 64

def classify_bump(prev, cur)
  pv = Gem::Version.new(prev)
  cv = Gem::Version.new(cur)
  return :same if cv == pv
  return :downgrade if cv < pv

  pmaj, pmin, ppatch = (pv.segments + [0, 0, 0])[0, 3]
  cmaj, cmin, cpatch = (cv.segments + [0, 0, 0])[0, 3]

  if cmaj > pmaj
    return :epic if cmaj && cmaj > 1000

    :major
  elsif cmin > pmin
    :minor
  elsif cpatch > ppatch
    :patch
  else
    # Fallback; should be covered by :same above, but in case of weird segment shapes
    :same
  end
end

.detect_version(root, override: nil) ⇒ String

Detects a unique VERSION constant declared under lib/**/version.rb, or in K_CHANGELOG_VERSION_FILE when a monorepo/root changelog needs to point at a representative package version file.

Parameters:

  • root (String)

    project root

  • override (String, nil) (defaults to: nil)

    explicit version supplied by caller

Returns:

  • (String)

    version string



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/kettle/dev/versioning.rb', line 15

def detect_version(root, override: nil)
  explicit = normalize_explicit_version(override)
  return explicit if explicit

  candidates = version_file_candidates(root)
  versions = candidates.map do |path|
    content = File.read(path)
    m = content.match(/VERSION\s*=\s*(["'])([^"']+)\1/)
    next unless m

    m[2]
  end.compact
  abort!("VERSION constant not found in #{root}/lib/**/version.rb") if versions.none?
  abort!("Multiple VERSION constants found to be out of sync (#{versions.inspect}) in #{root}/lib/**/version.rb") unless versions.uniq.length == 1
  versions.first
end

.epic_major?(major) ⇒ Boolean

Whether MAJOR is an EPIC version (strictly > 1000)

Parameters:

  • major (Integer)

Returns:

  • (Boolean)


90
91
92
# File 'lib/kettle/dev/versioning.rb', line 90

def epic_major?(major)
  major && major > 1000
end

.normalize_explicit_version(value) ⇒ Object



32
33
34
35
36
37
38
39
40
# File 'lib/kettle/dev/versioning.rb', line 32

def normalize_explicit_version(value)
  version = value.to_s.strip
  return nil if version.empty?

  Gem::Version.new(version)
  version
rescue ArgumentError
  abort!("Invalid version override: #{version.inspect}")
end

.version_file_candidates(root) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/kettle/dev/versioning.rb', line 42

def version_file_candidates(root)
  override = ENV.fetch("K_CHANGELOG_VERSION_FILE", "").to_s.strip
  unless override.empty?
    path = File.expand_path(override, root)
    abort!("K_CHANGELOG_VERSION_FILE does not exist: #{override}") unless File.file?(path)

    return [path]
  end

  declared_path = Kettle::Dev::GemSpecReader.declared_version_file_path(root)
  return [declared_path] if declared_path

  candidates = Dir[File.join(root, "lib", "**", "version.rb")]
  abort!("Could not find version.rb under lib/**.") if candidates.empty?
  candidates
end