Class: Makit::Version
- Inherits:
-
Object
- Object
- Makit::Version
- Defined in:
- lib/makit/version.rb,
lib/makit/version_util.rb
Overview
Version utility class for handling version comparisons
Class Method Summary collapse
-
.bump ⇒ String
Bump the patch version in the SSOT version file.
-
.detect_from_file(filename, regex) ⇒ String?
Detect version using a regex pattern in a specific file.
-
.extract_version_from_ssot_file(file_path) ⇒ Object
Extract version from SSOT file based on file type.
-
.find_project_root(start_dir) ⇒ Object
Find project root by looking for common markers.
-
.find_ssot_version_file(project_root) ⇒ String?
Find the SSOT version file in the project root.
-
.get_highest_version(versions) ⇒ String?
Get the highest version from a list of version strings.
-
.get_version_from_file(path) ⇒ String
Extract version number from a file based on its extension.
-
.info ⇒ nil
Display version information for the current project.
-
.parse(version_string) ⇒ Hash
Parse a semantic version string into its components.
-
.set_version_in_file(filename, version) ⇒ nil
Update version number in a file based on its extension.
-
.set_version_in_files(glob_pattern, version) ⇒ nil
Update version number in multiple files matching a glob pattern.
-
.version ⇒ String
Attempt to detect the version from the gemspec file First tries to find a gemspec in the current project root Falls back to makit.gemspec only if we’re in the makit gem directory.
Class Method Details
.bump ⇒ String
Bump the patch version in the SSOT version file
Finds the Single Source of Truth (SSOT) version file in the project root, reads the current version, increments the patch version, and updates the file.
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/makit/version.rb', line 349 def self.bump # Find the SSOT version file project_root = begin require_relative "directories" unless defined?(Makit::Directories) Makit::Directories::PROJECT_ROOT rescue NameError, LoadError find_project_root(Dir.pwd) end raise "Project root not found" if project_root.nil? || !Dir.exist?(project_root) version_file = find_ssot_version_file(project_root) if version_file.nil? warn " Warning: No version file found in project root: #{project_root}" warn " You may define a constant VERSION_FILE to manually set the version file path" raise "Cannot bump version: no version file found" end # Read current version current_version = extract_version_from_ssot_file(version_file) raise "Version not found in #{version_file}" if current_version.nil? # Parse and bump patch version parsed = parse(current_version) new_version = "#{parsed[:major]}.#{parsed[:minor]}.#{parsed[:patch] + 1}" # Update the version file set_version_in_file(version_file, new_version) # Verify the update updated_version = extract_version_from_ssot_file(version_file) if updated_version != new_version raise "Version bump failed: expected #{new_version}, got #{updated_version}" end new_version end |
.detect_from_file(filename, regex) ⇒ String?
Detect version using a regex pattern in a specific file
226 227 228 229 230 231 |
# File 'lib/makit/version.rb', line 226 def self.detect_from_file(filename, regex) raise "unable to find version in #{filename}" unless File.exist?(filename) match = File.read(filename).match(regex) match.captures[0] if !match.nil? && match.captures.length.positive? end |
.extract_version_from_ssot_file(file_path) ⇒ Object
Extract version from SSOT file based on file type
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/makit/version.rb', line 440 def self.extract_version_from_ssot_file(file_path) case File.basename(file_path) when /\.gemspec$/ # Extract from gemspec: spec.version = "x.y.z" content = File.read(file_path) match = content.match(/spec\.version\s*=\s*["']([^"']+)["']/) match ? match[1] : nil when "Directory.Build.props" # Extract from Directory.Build.props: <Version>x.y.z</Version> content = File.read(file_path) match = content.match(%r{<Version>([^<]+)</Version>}) match ? match[1] : nil when "Cargo.toml" # Extract from Cargo.toml: version = "x.y.z" content = File.read(file_path) match = content.match(/version\s*=\s*["']([^"']+)["']/) match ? match[1] : nil when "package.json" # Extract from package.json: "version": "x.y.z" require "json" json = JSON.parse(File.read(file_path)) json["version"] when "pyproject.toml" # Extract from pyproject.toml: version = "x.y.z" (in [project] or [tool.poetry] section) content = File.read(file_path) # Try [project] section first match = content.match(/\[project\]\s*version\s*=\s*["']([^"']+)["']/) return match[1] if match # Try [tool.poetry] section match = content.match(/\[tool\.poetry\]\s*version\s*=\s*["']([^"']+)["']/) match ? match[1] : nil when "pom.xml" # Extract from pom.xml: <version>x.y.z</version> content = File.read(file_path) match = content.match(%r{<version>([^<]+)</version>}) match ? match[1] : nil else nil end end |
.find_project_root(start_dir) ⇒ Object
Find project root by looking for common markers
426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/makit/version.rb', line 426 def self.find_project_root(start_dir) current = File.(start_dir) root = File.("/") while current != root markers = ["Rakefile", "rakefile.rb", ".gitignore", ".git"] return current if markers.any? { |marker| File.exist?(File.join(current, marker)) } current = File.dirname(current) end nil end |
.find_ssot_version_file(project_root) ⇒ String?
Find the SSOT version file in the project root
Checks for VERSION_FILE constant first (if defined), then searches for common version files.
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 |
# File 'lib/makit/version.rb', line 395 def self.find_ssot_version_file(project_root) # Check for manually defined VERSION_FILE constant first if defined?(VERSION_FILE) && !VERSION_FILE.nil? version_file = File.(VERSION_FILE, project_root) return version_file if File.exist?(version_file) warn " Warning: VERSION_FILE constant points to non-existent file: #{VERSION_FILE}" end # Priority order for version files (SSOT) version_file_patterns = [ "*.gemspec", # Ruby gems "Directory.Build.props", # .NET projects "Cargo.toml", # Rust projects "package.json", # Node.js projects "pyproject.toml", # Python projects "pom.xml" # Maven/Java projects ] version_file_patterns.each do |pattern| matches = Dir.glob(File.join(project_root, pattern)) next if matches.empty? # For gemspec, prefer the one matching the project name or take the first version_file = matches.first return version_file if version_file end nil end |
.get_highest_version(versions) ⇒ String?
Get the highest version from a list of version strings
188 189 190 |
# File 'lib/makit/version.rb', line 188 def self.get_highest_version(versions) versions.max { |a, b| Gem::Version.new(a) <=> Gem::Version.new(b) } end |
.get_version_from_file(path) ⇒ String
Extract version number from a file based on its extension
Supports multiple file formats:
-
.csproj files: ‘<Version>x.y.z</Version>`
-
.wxs files: ‘Version=“x.y.z”`
-
.yml files: ‘VERSION: “x.y.z”`
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/makit/version.rb', line 202 def self.get_version_from_file(path) raise "file #{path}does not exist" unless File.exist?(path) extension = File.extname(path) case extension when ".csproj" Makit::Version.detect_from_file(path, /<Version>([-\w\d.]+)</) when ".wxs" Makit::Version.detect_from_file(path, / Version="([\d.]+)"/) when ".yml" Makit::Version.detect_from_file(path, /VERSION:\s*["']?([\d.]+)["']?/) when ".rb" Makit::Version.detect_from_file(path, /VERSION = "([\d.]+)"/) else raise "unrecognized file type" end end |
.info ⇒ nil
Display version information for the current project
Finds the Single Source of Truth (SSOT) version file in the project root and displays the file path and current version value.
Searches for common version files in priority order:
-
*.gemspec (Ruby gems)
-
Directory.Build.props (.NET projects)
-
Cargo.toml (Rust projects)
-
package.json (Node.js projects)
-
pyproject.toml (Python projects)
-
pom.xml (Maven/Java projects)
If no version file is found, issues a warning and suggests defining a VERSION_FILE constant to manually specify the version file path.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/makit/version.rb', line 299 def self.info # Access Directories lazily to avoid circular dependency project_root = begin require_relative "directories" unless defined?(Makit::Directories) Makit::Directories::PROJECT_ROOT rescue NameError, LoadError # Fallback: try to find project root from current directory find_project_root(Dir.pwd) end raise "Project root not found" if project_root.nil? || !Dir.exist?(project_root) version_file = find_ssot_version_file(project_root) if version_file.nil? warn " Warning: No version file found in project root: #{project_root}" warn " You may define a constant VERSION_FILE to manually set the version file path" return end # Extract version based on file type version = extract_version_from_ssot_file(version_file) raise "Version not found in #{version_file}" if version.nil? # Display information with relative path relative_path = version_file.sub(project_root + "/", "") puts " Version File: #{relative_path}" puts " Version: #{version}" end |
.parse(version_string) ⇒ Hash
Parse a semantic version string into its components
Parses a version string following Semantic Versioning (SemVer) format: MAJOR.MINOR.PATCH
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/makit/version.rb', line 147 def self.parse(version_string) parts = version_string.split(".") if parts.length < 3 raise "Invalid version format: #{version_string}. Expected format: Major.Minor.Patch" end major = parts[0].to_i minor = parts[1].to_i patch = parts[2].to_i # Handle pre-release suffixes (e.g., "0.1.0-preview" -> patch = 0, suffix = "-preview") # Note: If suffix contains dots (e.g., "1.2.3-preview.1"), the split by "." will separate # the patch and suffix parts, so only the part before the first "-" in parts[2] is used as patch. patch_part = parts[2] if patch_part.include?("-") patch, suffix = patch_part.split("-", 2) patch = patch.to_i suffix = "-#{suffix}" else suffix = "" end { major: major, minor: minor, patch: patch, suffix: suffix } end |
.set_version_in_file(filename, version) ⇒ nil
Update version number in a file based on its extension
Supports updating versions in multiple file formats:
-
.yml files
-
.gemspec files
-
.csproj files
-
.nuspec files
-
.wxs files
-
.toml files
246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/makit/version.rb', line 246 def self.set_version_in_file(filename, version) text = File.read(filename) # VERSION = "0.0.138rake" (.rb file) new_text = text new_text = text.gsub(/VERSION:\s?['|"]([.\d]+)['|"]/, "VERSION: \"#{version}\"") if filename.include?(".yml") new_text = text.gsub(/version\s?=\s?['|"]([.\d]+)['|"]/, "version='#{version}'") if filename.include?(".gemspec") new_text = text.gsub(/<Version>([-\w\d.]+)</, "<Version>#{version}<") if filename.include?(".csproj") new_text = text.gsub(/<version>([-\w\d.]+)</, "<version>#{version}<") if filename.include?(".nuspec") new_text = text.gsub(/ Version="([\d.]+)"/, " Version=\"#{version}\"") if filename.include?(".wxs") new_text = text.gsub(/VERSION = "([\d.]+)"/, "VERSION = \"#{version}\"") if filename.include?(".rb") new_text = text.gsub(/version\s+=\s+['"]([\w.]+)['"]/, "version=\"#{version}\"") if filename.include?(".toml") File.write(filename, new_text) if new_text != text end |
.set_version_in_files(glob_pattern, version) ⇒ nil
Update version number in multiple files matching a glob pattern
265 266 267 268 269 |
# File 'lib/makit/version.rb', line 265 def self.set_version_in_files(glob_pattern, version) Dir.glob(glob_pattern).each do |filename| set_version_in_file(filename, version) end end |
.version ⇒ String
Attempt to detect the version from the gemspec file First tries to find a gemspec in the current project root Falls back to makit.gemspec only if we’re in the makit gem directory
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/makit/version.rb', line 70 def self.version # Try to find gemspec in project root first (for projects using makit) project_root = begin require_relative "directories" unless defined?(Makit::Directories) Makit::Directories::PROJECT_ROOT rescue NameError, LoadError find_project_root(Dir.pwd) end # Look for any .gemspec file in project root if project_root && Dir.exist?(project_root) gemspec_files = Dir.glob(File.join(project_root, "*.gemspec")) if gemspec_files.any? gemspec = gemspec_files.first gemspec_content = File.read(gemspec) match = gemspec_content.match(/spec\.version\s*=\s*["']([^"']+)["']/) return match[1] if match end end # Fallback to makit.gemspec (for the makit gem itself) # Check if makit.gemspec exists relative to this file makit_gemspec = File.join(File.dirname(__FILE__), "..", "..", "makit.gemspec") makit_gemspec = File.(makit_gemspec) # Use makit.gemspec if: # 1. It exists, AND # 2. Either we're in the makit gem directory (project_root matches), OR # project_root is nil (running outside a project context, likely from installed gem) if File.exist?(makit_gemspec) makit_gem_dir = File.dirname(makit_gemspec) use_makit_gemspec = if project_root # If we have a project root, only use makit.gemspec if we're in the makit gem directory File.(project_root) == File.(makit_gem_dir) else # If no project root, check if current directory is makit gem or if makit.gemspec is nearby # This handles the case when running from installed gem or outside project context Dir.pwd == makit_gem_dir || File.(Dir.pwd).start_with?(File.(makit_gem_dir)) end if use_makit_gemspec gemspec_content = File.read(makit_gemspec) match = gemspec_content.match(/spec\.version\s*=\s*["']([^"']+)["']/) raise "Version not found in gemspec file" if match.nil? return match[1] end end # If no gemspec found, return default version (for non-gem projects) "0.0.0" end |