discourse/lib/release_utils/version.rb
Loïc Guitaut f77f59a5c1
DEV: Bump version automatically when cutting release branches (#38504)
During recent refactoring, we lost the ability to bump versions on
release branches. Rather than restoring the separate workflow and rake
task that existed before, this integrates version bumping directly into
the existing release automation:

1. When `release:maybe_cut_branch` creates a new release branch, it now
strips the `-latest` suffix and commits the release version directly
(e.g., `2025.1.0-latest` → `2025.1.0`).

2. When `release:stage_security_fixes` targets a release branch, it now
bumps the patch version automatically (e.g., `2025.6.0` → `2025.6.1`).

For non-security patch releases, the existing workflow handles tagging
automatically when commits are pushed to release branches with a
manually bumped version.
2026-03-12 12:05:28 +01:00

100 lines
2.2 KiB
Ruby

# frozen_string_literal: true
module ReleaseUtils
class Version
include Comparable
PRE_RELEASE = "latest"
attr_reader :major, :minor, :patch, :pre, :revision
protected attr_reader :gem_version
def initialize(version_string)
@gem_version = Gem::Version.new(version_string)
segments = gem_version.segments
@major = segments[0]
@minor = segments[1] || 1
@patch = segments[2] || 0
@pre, @revision = segments.drop(3).drop_while { it == "pre" } if gem_version.prerelease?
freeze
end
class << self
def current
version_string = File.read("lib/version.rb")[/STRING = "(.*)"/, 1]
raise "Unable to parse current version from lib/version.rb" unless version_string
new(version_string)
end
def next
target = new("#{Time.current.strftime("%Y.%-m")}.0-#{PRE_RELEASE}")
return target if target > current
current.next_development_cycle
end
end
def <=>(other)
other = self.class.new(other) if other.is_a?(String)
return unless other.is_a?(self.class)
gem_version <=> other.gem_version
end
def same_development_cycle?(other)
development? && other.development? && without_revision == other.without_revision
end
def same_series?(other)
series == other.series
end
def development?
pre == PRE_RELEASE
end
def series
"#{major}.#{minor}"
end
def branch_name
"release/#{series}"
end
def tag_name
"v#{self}"
end
def without_revision
return self unless revision
self.class.new("#{major}.#{minor}.#{patch}-#{pre}")
end
def next_development_cycle
carry, new_minor = minor.divmod(12)
self.class.new("#{major + carry}.#{new_minor + 1}.0-#{PRE_RELEASE}")
end
def next_revision
self.class.new("#{major}.#{minor}.#{patch}-#{pre}.#{revision.to_i + 1}")
end
def release_version
self.class.new("#{major}.#{minor}.#{patch}")
end
def next_patch
self.class.new("#{major}.#{minor}.#{patch + 1}")
end
def to_s
"#{major}.#{minor}.#{patch}#{"-#{pre}" if pre}#{".#{revision}" if revision}"
end
def inspect
"#<#{self.class} #{self}>"
end
end
end