version_boss 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +36 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +119 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +199 -0
- data/Rakefile +92 -0
- data/exe/gem-version-boss +6 -0
- data/lib/version_boss/gem/command_line.rb +443 -0
- data/lib/version_boss/gem/incrementable_version.rb +216 -0
- data/lib/version_boss/gem/regexp.rb +29 -0
- data/lib/version_boss/gem/version.rb +449 -0
- data/lib/version_boss/gem/version_file.rb +103 -0
- data/lib/version_boss/gem/version_file_factory.rb +40 -0
- data/lib/version_boss/gem/version_file_sources/base.rb +56 -0
- data/lib/version_boss/gem/version_file_sources/gemspec.rb +40 -0
- data/lib/version_boss/gem/version_file_sources/version.rb +36 -0
- data/lib/version_boss/gem/version_file_sources/version_rb.rb +39 -0
- data/lib/version_boss/gem/version_file_sources.rb +14 -0
- data/lib/version_boss/gem.rb +14 -0
- data/lib/version_boss/semver/command_line.rb +474 -0
- data/lib/version_boss/semver/incrementable_version.rb +192 -0
- data/lib/version_boss/semver/regexp.rb +40 -0
- data/lib/version_boss/semver/version.rb +374 -0
- data/lib/version_boss/semver.rb +9 -0
- data/lib/version_boss/version.rb +6 -0
- data/lib/version_boss.rb +10 -0
- data/version_boss.gemspec +56 -0
- metadata +243 -0
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'version'
|
4
|
+
|
5
|
+
module VersionBoss
|
6
|
+
module Semver
|
7
|
+
# A Semver Version with additional constraints on the pre-release part of the version
|
8
|
+
#
|
9
|
+
# An IncrementableVersion is valid if one of the two following conditions is met:
|
10
|
+
# 1. The pre-release part is empty
|
11
|
+
# 2. The pre-release part is composed of two dot-separated identifiers:
|
12
|
+
# * the first being a String representing the pre-release type (e.g. 'alpha',
|
13
|
+
# 'beta', etc.). The default pre-release type is 'pre'
|
14
|
+
# * the second being an Integer representing the pre-release sequence number
|
15
|
+
# (starting at 1)
|
16
|
+
#
|
17
|
+
# Valid versions with pre-release parts: `1.2.3-alpha.1`, `1.2.3-beta.2`, `1.2.3-pre.3`
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
#
|
21
|
+
class IncrementableVersion < Version
|
22
|
+
# Create a new IncrementableVersion object
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# IncrementableVersion.new('1.2.3').valid? # => true
|
26
|
+
# IncrementableVersion.new('1.2.3-alpha.1+build.001').valid? # => true
|
27
|
+
# IncrementableVersion.new('1.2.3-alpha').valid? # => raise VersionBoss::Error
|
28
|
+
# IncrementableVersion.new('1.2.3-alpha.1.2').valid? # => raise VersionBoss::Error
|
29
|
+
# IncrementableVersion.new('1.2.3-alpha.one').valid? # => raise VersionBoss::Error
|
30
|
+
#
|
31
|
+
# @return [Boolean] true if the version string is a valid semver and meets the conditions above
|
32
|
+
#
|
33
|
+
def valid?
|
34
|
+
super && (
|
35
|
+
pre_release.empty? ||
|
36
|
+
(
|
37
|
+
pre_release_identifiers.size == 2 &&
|
38
|
+
pre_type.is_a?(String) &&
|
39
|
+
pre_number.is_a?(Integer)
|
40
|
+
)
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
# The default pre-release identifier
|
45
|
+
DEFAULT_PRE_TYPE = 'pre'
|
46
|
+
|
47
|
+
# Increment the major version
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# IncrementableVersionify::Semver.new('1.2.3').next_major # => IncrementableVersionify::Semver.new('2.0.0')
|
51
|
+
#
|
52
|
+
# @return [IncrementableVersion] a new IncrementableVersion object with the major version incremented
|
53
|
+
#
|
54
|
+
def next_major(pre: false, pre_type: DEFAULT_PRE_TYPE, build_metadata: nil)
|
55
|
+
version_string = "#{major.to_i + 1}.0.0"
|
56
|
+
version_string += "-#{pre_type}.1" if pre
|
57
|
+
build_metadata = self.build_metadata if build_metadata.nil?
|
58
|
+
version_string += "+#{build_metadata}" unless build_metadata.empty?
|
59
|
+
IncrementableVersion.new(version_string)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Increment the minor version
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# IncrementableVersionify::Semver.new('1.2.3').next_minor # => IncrementableVersionify::Semver.new('1.3.0')
|
66
|
+
#
|
67
|
+
# @return [IncrementableVersion] a new IncrementableVersion object with the major version incremented
|
68
|
+
#
|
69
|
+
def next_minor(pre: false, pre_type: DEFAULT_PRE_TYPE, build_metadata: nil)
|
70
|
+
version_string = "#{major}.#{minor.to_i + 1}.0"
|
71
|
+
version_string += "-#{pre_type}.1" if pre
|
72
|
+
build_metadata = self.build_metadata if build_metadata.nil?
|
73
|
+
version_string += "+#{build_metadata}" unless build_metadata.empty?
|
74
|
+
IncrementableVersion.new(version_string)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Increment the patch version
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# IncrementableVersionify::Semver.new('1.2.3').next_patch # => IncrementableVersionify::Semver.new('1.2.1')
|
81
|
+
#
|
82
|
+
# @return [IncrementableVersion] a new IncrementableVersion object with the patch part incremented
|
83
|
+
#
|
84
|
+
def next_patch(pre: false, pre_type: DEFAULT_PRE_TYPE, build_metadata: nil)
|
85
|
+
version_string = "#{major}.#{minor}.#{patch.to_i + 1}"
|
86
|
+
version_string += "-#{pre_type}.1" if pre
|
87
|
+
build_metadata = self.build_metadata if build_metadata.nil?
|
88
|
+
version_string += "+#{build_metadata}" unless build_metadata.empty?
|
89
|
+
IncrementableVersion.new(version_string)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Increment the pre_release part of the version
|
93
|
+
#
|
94
|
+
# @example
|
95
|
+
# IncrementableVersionify::Semver.new('1.2.3-pre.1').next_patch.to_s # => '1.2.3-pre.2'
|
96
|
+
#
|
97
|
+
# @return [IncrementableVersion] a new IncrementableVersion object with the pre_release part incremented
|
98
|
+
#
|
99
|
+
def next_pre(pre_type: nil, build_metadata: nil)
|
100
|
+
assert_is_a_pre_release_version
|
101
|
+
version_string = "#{major}.#{minor}.#{patch}"
|
102
|
+
version_string += next_pre_part(pre_type)
|
103
|
+
build_metadata ||= self.build_metadata
|
104
|
+
version_string += "+#{build_metadata}" unless build_metadata.empty?
|
105
|
+
IncrementableVersion.new(version_string)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Drop the pre-release part of the version
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# IncrementableVersionify::Semver.new('1.2.3-pre.1').next_release.to_s # => '1.2.3'
|
112
|
+
#
|
113
|
+
# @return [IncrementableVersion] a new IncrementableVersion object with the pre_release part dropped
|
114
|
+
# @raise [VersionBoss::Error] if the version is not a pre-release version
|
115
|
+
#
|
116
|
+
def next_release(build_metadata: nil)
|
117
|
+
assert_is_a_pre_release_version
|
118
|
+
version_string = "#{major}.#{minor}.#{patch}"
|
119
|
+
build_metadata ||= self.build_metadata
|
120
|
+
version_string += "+#{build_metadata}" unless build_metadata.empty?
|
121
|
+
IncrementableVersion.new(version_string)
|
122
|
+
end
|
123
|
+
|
124
|
+
# The pre-release type (for example, 'alpha', 'beta', 'pre', etc.)
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
# IncrementableVersionify::Semver.new('1.2.3-pre.1').pre_type # => 'pre'
|
128
|
+
#
|
129
|
+
# @return [String]
|
130
|
+
#
|
131
|
+
def pre_type
|
132
|
+
pre_release_identifiers[0]
|
133
|
+
end
|
134
|
+
|
135
|
+
# The pre-release sequence number
|
136
|
+
#
|
137
|
+
# The pre-release sequence number starts at 1 for each pre-release type.
|
138
|
+
#
|
139
|
+
# @example
|
140
|
+
# IncrementableVersionify::Semver.new('1.2.3-pre.1').pre_number # => 1
|
141
|
+
#
|
142
|
+
# @return [Integer]
|
143
|
+
#
|
144
|
+
def pre_number
|
145
|
+
pre_release_identifiers[1]
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
# Raise an error if the version is not a pre-release version
|
151
|
+
#
|
152
|
+
# @return [void]
|
153
|
+
# @raise [VersionBoss::Error] if the version is not a pre-release version
|
154
|
+
# @api private
|
155
|
+
def assert_is_a_pre_release_version
|
156
|
+
return unless pre_release.empty?
|
157
|
+
|
158
|
+
raise VersionBoss::Error, 'Cannot increment the pre-release part of a release version'
|
159
|
+
end
|
160
|
+
|
161
|
+
# Raise an error if new pre-release type is less than the old pre-release type
|
162
|
+
#
|
163
|
+
# If the new pre-release type is lexically less than the old pre-release type,
|
164
|
+
# then raise an error because the resulting error would sort before the existing
|
165
|
+
# version.
|
166
|
+
#
|
167
|
+
# @return [void]
|
168
|
+
# @raise [VersionBoss::Error] if the pre-release type is not valid
|
169
|
+
# @api private
|
170
|
+
def assert_pre_type_is_valid(pre_type)
|
171
|
+
return if self.pre_type <= pre_type
|
172
|
+
|
173
|
+
message = 'Cannot increment the pre-release identifier ' \
|
174
|
+
"from '#{self.pre_type}' to '#{pre_type}' " \
|
175
|
+
"because '#{self.pre_type}' is lexically less than '#{pre_type}'"
|
176
|
+
|
177
|
+
raise VersionBoss::Error, message
|
178
|
+
end
|
179
|
+
|
180
|
+
# Return the next pre-release part
|
181
|
+
# @param pre_type [String, nil] the given pre-release type or nil to use the existing pre-release type
|
182
|
+
# @return [String]
|
183
|
+
# @api private
|
184
|
+
def next_pre_part(pre_type)
|
185
|
+
pre_type ||= self.pre_type
|
186
|
+
assert_pre_type_is_valid(pre_type)
|
187
|
+
next_pre_number = self.pre_type == pre_type ? (pre_number + 1) : 1
|
188
|
+
"-#{pre_type}.#{next_pre_number}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VersionBoss
|
4
|
+
module Semver
|
5
|
+
# Match a semver within a string
|
6
|
+
REGEXP = /
|
7
|
+
(?<semver>
|
8
|
+
(?<major>0|[1-9]\d*)
|
9
|
+
\.
|
10
|
+
(?<minor>0|[1-9]\d*)
|
11
|
+
\.
|
12
|
+
(?<patch>0|[1-9]\d*)
|
13
|
+
(?:-
|
14
|
+
(?<pre_release>
|
15
|
+
(?:
|
16
|
+
0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*
|
17
|
+
)
|
18
|
+
(?:
|
19
|
+
\.
|
20
|
+
(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)
|
21
|
+
)*
|
22
|
+
)
|
23
|
+
)?
|
24
|
+
(?:
|
25
|
+
\+
|
26
|
+
(?<build_metadata>
|
27
|
+
[0-9a-zA-Z-]+
|
28
|
+
(?:
|
29
|
+
\.
|
30
|
+
[0-9a-zA-Z-]+
|
31
|
+
)*
|
32
|
+
)
|
33
|
+
)?
|
34
|
+
)
|
35
|
+
/x
|
36
|
+
|
37
|
+
# Match a semver to the full string
|
38
|
+
REGEXP_FULL = /\A#{REGEXP.source}\z/x
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,374 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'regexp'
|
4
|
+
|
5
|
+
module VersionBoss
|
6
|
+
module Semver
|
7
|
+
# Parse and compare semver version strings
|
8
|
+
#
|
9
|
+
# This class will parse a semver version string that complies to Semantic
|
10
|
+
# Versioning 2.0.0.
|
11
|
+
#
|
12
|
+
# Two Semver objects can be compared using the spaceship operator (<=>)
|
13
|
+
# according to the rules of Semantic Versioning 2.0.0.
|
14
|
+
#
|
15
|
+
# @example Basic version parsing
|
16
|
+
# semver = VersionBoss::Semver.new('1.2.3')
|
17
|
+
# semver.major # => '1'
|
18
|
+
# semver.minor # => '2'
|
19
|
+
# semver.patch # => '3'
|
20
|
+
#
|
21
|
+
# @example Parsing a version with a pre-release identifier
|
22
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1')
|
23
|
+
# semver.pre_release # => 'alpha.1'
|
24
|
+
# semver.pre_release_identifiers # => ['alpha', '1']
|
25
|
+
#
|
26
|
+
# @example A version with build metadata
|
27
|
+
# semver = VersionBoss::Semver.new('1.2.3+build.1')
|
28
|
+
# semver.build_metadata # => 'build.1'
|
29
|
+
#
|
30
|
+
# @example Comparing versions
|
31
|
+
# semver1 = VersionBoss::Semver.new('1.2.3')
|
32
|
+
# semver2 = VersionBoss::Semver.new('1.2.4')
|
33
|
+
# semver1 <=> semver2 # => true
|
34
|
+
#
|
35
|
+
# See the Semantic Versioning 2.0.0 specification for more details.
|
36
|
+
#
|
37
|
+
# @see https://semver.org/spec/v2.0.0.html Semantic Versioning 2.0.0
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
#
|
41
|
+
class Version
|
42
|
+
include Comparable
|
43
|
+
|
44
|
+
# Create a new Semver object
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# version = VersionBoss::Semver.new('1.2.3-alpha.1')
|
48
|
+
#
|
49
|
+
# @param version [String] The version string to parse
|
50
|
+
#
|
51
|
+
# @raise [VersionBoss::Error] version is not a string or not a valid semver version
|
52
|
+
#
|
53
|
+
def initialize(version)
|
54
|
+
assert_version_must_be_a_string(version)
|
55
|
+
@version = version
|
56
|
+
parse
|
57
|
+
assert_valid_version
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!attribute version [r]
|
61
|
+
#
|
62
|
+
# The complete version string
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1+build.001')
|
66
|
+
# semver.version #=> '1.2.3-alpha.1+build.001'
|
67
|
+
#
|
68
|
+
# @return [String]
|
69
|
+
#
|
70
|
+
# @api public
|
71
|
+
#
|
72
|
+
attr_reader :version
|
73
|
+
|
74
|
+
# @attribute major [r]
|
75
|
+
#
|
76
|
+
# The major part of the version
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1+build.001')
|
80
|
+
# semver.major #=> '1'
|
81
|
+
#
|
82
|
+
# @return [String]
|
83
|
+
#
|
84
|
+
# @api public
|
85
|
+
#
|
86
|
+
attr_reader :major
|
87
|
+
|
88
|
+
# @attribute minor [r]
|
89
|
+
#
|
90
|
+
# The minor part of the version
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1+build.001')
|
94
|
+
# semver.minor #=> '2'
|
95
|
+
#
|
96
|
+
# @return [String]
|
97
|
+
#
|
98
|
+
# @api public
|
99
|
+
#
|
100
|
+
attr_reader :minor
|
101
|
+
|
102
|
+
# @attribute patch [r]
|
103
|
+
#
|
104
|
+
# The patch part of the version
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1+build.001')
|
108
|
+
# semver.patch #=> '3'
|
109
|
+
#
|
110
|
+
# @return [String]
|
111
|
+
#
|
112
|
+
# @api public
|
113
|
+
#
|
114
|
+
attr_reader :patch
|
115
|
+
|
116
|
+
# @attribute pre_release [r]
|
117
|
+
#
|
118
|
+
# The pre_release part of the version
|
119
|
+
#
|
120
|
+
# Will be an empty string if the version has no pre_release part.
|
121
|
+
#
|
122
|
+
# @example
|
123
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1+build.001')
|
124
|
+
# semver.pre_release #=> 'alpha.1'
|
125
|
+
#
|
126
|
+
# @example When the version has no pre_release part
|
127
|
+
# semver = VersionBoss::Semver.new('1.2.3')
|
128
|
+
# semver.pre_release #=> ''
|
129
|
+
#
|
130
|
+
# @return [String]
|
131
|
+
#
|
132
|
+
# @api public
|
133
|
+
#
|
134
|
+
attr_reader :pre_release
|
135
|
+
|
136
|
+
# @attribute pre_release_identifiers [r]
|
137
|
+
#
|
138
|
+
# The pre_release identifiers of the version
|
139
|
+
#
|
140
|
+
# @example
|
141
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1+build.001')
|
142
|
+
# semver.pre_release_identifiers #=> ['alpha', '1']
|
143
|
+
#
|
144
|
+
# @example When the version has no pre_release part
|
145
|
+
# semver = VersionBoss::Semver.new('1.2.3')
|
146
|
+
# semver.pre_release_identifiers #=> []
|
147
|
+
#
|
148
|
+
# @return [Array<String>]
|
149
|
+
#
|
150
|
+
# @api public
|
151
|
+
#
|
152
|
+
attr_reader :pre_release_identifiers
|
153
|
+
|
154
|
+
# @attribute build_metadata [r]
|
155
|
+
#
|
156
|
+
# The build_metadata part of the version
|
157
|
+
#
|
158
|
+
# Will be an empty string if the version has no build_metadata part.
|
159
|
+
#
|
160
|
+
# @example
|
161
|
+
# semver = VersionBoss::Semver.new('1.2.3-alpha.1+build.001')
|
162
|
+
# semver.build_metadata #=> 'build.001'
|
163
|
+
#
|
164
|
+
# @example When the version has no build_metadata part
|
165
|
+
# semver = VersionBoss::Semver.new('1.2.3')
|
166
|
+
# semver.build_metadata #=> ''
|
167
|
+
#
|
168
|
+
# @return [String]
|
169
|
+
#
|
170
|
+
# @api public
|
171
|
+
#
|
172
|
+
attr_reader :build_metadata
|
173
|
+
|
174
|
+
# Compare two Semver objects
|
175
|
+
#
|
176
|
+
# See the [Precedence Rules](https://semver.org/spec/v2.0.0.html#spec-item-11)
|
177
|
+
# in the Semantic Versioning 2.0.0 Specification for more details.
|
178
|
+
#
|
179
|
+
# @example
|
180
|
+
# semver1 = VersionBoss::Semver.new('1.2.3')
|
181
|
+
# semver2 = VersionBoss::Semver.new('1.2.4')
|
182
|
+
# semver1 <=> semver2 # => -1
|
183
|
+
# semver2 <=> semver1 # => 1
|
184
|
+
#
|
185
|
+
# @example A Semver is equal to itself
|
186
|
+
# semver1 = VersionBoss::Semver.new('1.2.3')
|
187
|
+
# semver1 <=> semver1 # => 0
|
188
|
+
#
|
189
|
+
# @example A pre-release version is always older than a normal version
|
190
|
+
# semver1 = VersionBoss::Semver.new('1.2.3-alpha.1')
|
191
|
+
# semver2 = VersionBoss::Semver.new('1.2.3')
|
192
|
+
# semver1 <=> semver2 # => -1
|
193
|
+
#
|
194
|
+
# @example Pre-releases are compared by the parts of the pre-release version
|
195
|
+
# semver1 = VersionBoss::Semver.new('1.2.3-alpha.1')
|
196
|
+
# semver2 = VersionBoss::Semver.new('1.2.3-alpha.2')
|
197
|
+
# semver1 <=> semver2 # => -1
|
198
|
+
#
|
199
|
+
# @example Build metadata is ignored when comparing versions
|
200
|
+
# semver1 = VersionBoss::Semver.new('1.2.3+build.100')
|
201
|
+
# semver2 = VersionBoss::Semver.new('1.2.3+build.101')
|
202
|
+
# semver1 <=> semver2 # => 0
|
203
|
+
#
|
204
|
+
# @param other [Semver] the other Semver to compare to
|
205
|
+
#
|
206
|
+
# @return [Integer] -1 if self < other, 0 if self == other, or 1 if self > other
|
207
|
+
#
|
208
|
+
# @raise [VersionBoss::Error] other is not a semver
|
209
|
+
#
|
210
|
+
def <=>(other)
|
211
|
+
assert_other_is_a_semver(other)
|
212
|
+
|
213
|
+
result = compare_core_parts(other)
|
214
|
+
|
215
|
+
return result unless result.zero? && pre_release != other.pre_release
|
216
|
+
return 1 if pre_release.empty?
|
217
|
+
return -1 if other.pre_release.empty?
|
218
|
+
|
219
|
+
compare_pre_release_part(other)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Determine if the version string is a valid semver
|
223
|
+
#
|
224
|
+
# Override this method in a subclass to provide extra or custom validation.
|
225
|
+
#
|
226
|
+
# @example
|
227
|
+
# VersionBoss::Semver.new('1.2.3').valid? # => true
|
228
|
+
# VersionBoss::Semver.new('1.2.3-alpha.1+build.001').valid? # => true
|
229
|
+
# VersionBoss::Semver.new('bogus').valid? # => raises VersionBoss::Error
|
230
|
+
#
|
231
|
+
# @return [Boolean] true if the version string is a valid semver
|
232
|
+
#
|
233
|
+
def valid?
|
234
|
+
# If major is set, then so is everything else
|
235
|
+
!major.nil?
|
236
|
+
end
|
237
|
+
|
238
|
+
# Two versions are equal if their version strings are equal
|
239
|
+
#
|
240
|
+
# @example
|
241
|
+
# VersionBoss::Semver.new('1.2.3') == '1.2.3' # => true
|
242
|
+
#
|
243
|
+
# @param other [Semver] the other Semver to compare to
|
244
|
+
#
|
245
|
+
# @return [Boolean] true if the version strings are equal
|
246
|
+
#
|
247
|
+
def ==(other)
|
248
|
+
version == other.to_s
|
249
|
+
end
|
250
|
+
|
251
|
+
# The string representation of a Semver is its version string
|
252
|
+
#
|
253
|
+
# @example
|
254
|
+
# VersionBoss::Semver.new('1.2.3').to_s # => '1.2.3'
|
255
|
+
#
|
256
|
+
# @return [String] the version string
|
257
|
+
#
|
258
|
+
def to_s
|
259
|
+
version
|
260
|
+
end
|
261
|
+
|
262
|
+
private
|
263
|
+
|
264
|
+
# Parse @version into its parts
|
265
|
+
# @return [void]
|
266
|
+
# @api private
|
267
|
+
def parse
|
268
|
+
return unless (match_data = version.match(REGEXP_FULL))
|
269
|
+
|
270
|
+
core_parts(match_data)
|
271
|
+
pre_release_part(match_data)
|
272
|
+
build_metadata_part(match_data)
|
273
|
+
end
|
274
|
+
|
275
|
+
# Compare the major, minor, and patch parts of this Semver to other
|
276
|
+
# @param other [Semver] the other Semver to compare to
|
277
|
+
# @return [Integer] -1 if self < other, 0 if self == other, or 1 if self > other
|
278
|
+
# @api private
|
279
|
+
def compare_core_parts(other)
|
280
|
+
identifiers = [major.to_i, minor.to_i, patch.to_i]
|
281
|
+
other_identifiers = [other.major.to_i, other.minor.to_i, other.patch.to_i]
|
282
|
+
|
283
|
+
identifiers <=> other_identifiers
|
284
|
+
end
|
285
|
+
|
286
|
+
# Compare two pre-release identifiers
|
287
|
+
#
|
288
|
+
# Implements the rules for precedence for comparing two pre-release identifiers
|
289
|
+
# from the Semantic Versioning 2.0.0 Specification.
|
290
|
+
#
|
291
|
+
# @param identifier [String, Integer] the identifier to compare
|
292
|
+
# @param other_identifier [String, Integer] the other identifier to compare
|
293
|
+
# @return [Integer] -1, 0, or 1
|
294
|
+
# @api private
|
295
|
+
def compare_identifiers(identifier, other_identifier)
|
296
|
+
return 1 if other_identifier.nil?
|
297
|
+
return -1 if identifier.is_a?(Integer) && other_identifier.is_a?(String)
|
298
|
+
return 1 if other_identifier.is_a?(Integer) && identifier.is_a?(String)
|
299
|
+
|
300
|
+
identifier <=> other_identifier
|
301
|
+
end
|
302
|
+
|
303
|
+
# Compare two pre-release version parts
|
304
|
+
#
|
305
|
+
# Implements the rules for precedence for comparing the pre-release part of
|
306
|
+
# one version with the pre-release part of another version from the Semantic
|
307
|
+
# Versioning 2.0.0 Specification.
|
308
|
+
#
|
309
|
+
# @param other [Semver] the other Semver to compare to
|
310
|
+
# @return [Integer] -1, 0, or 1
|
311
|
+
# @api private
|
312
|
+
def compare_pre_release_part(other)
|
313
|
+
pre_release_identifiers.zip(other.pre_release_identifiers).each do |field, other_field|
|
314
|
+
result = compare_identifiers(field, other_field)
|
315
|
+
return result unless result.zero?
|
316
|
+
end
|
317
|
+
pre_release_identifiers.size < other.pre_release_identifiers.size ? -1 : 0
|
318
|
+
end
|
319
|
+
|
320
|
+
# Raise a error if other is not a valid Semver
|
321
|
+
# @param other [Semver] the other to check
|
322
|
+
# @return [void]
|
323
|
+
# @raise [VersionBoss::Error] if other is not a valid Semver
|
324
|
+
# @api private
|
325
|
+
def assert_other_is_a_semver(other)
|
326
|
+
raise VersionBoss::Error, 'other must be a Semver' unless other.is_a?(VersionBoss::Semver::Version)
|
327
|
+
end
|
328
|
+
|
329
|
+
# Raise a error if the given version is not a string
|
330
|
+
# @param version [Semver] the version to check
|
331
|
+
# @return [void]
|
332
|
+
# @raise [VersionBoss::Error] if the given version is not a string
|
333
|
+
# @api private
|
334
|
+
def assert_version_must_be_a_string(version)
|
335
|
+
raise VersionBoss::Error, 'Version must be a string' unless version.is_a?(String)
|
336
|
+
end
|
337
|
+
|
338
|
+
# Raise a error if this version object is not a valid Semver
|
339
|
+
# @return [void]
|
340
|
+
# @raise [VersionBoss::Error] if other is not a valid Semver
|
341
|
+
# @api private
|
342
|
+
def assert_valid_version
|
343
|
+
raise VersionBoss::Error, "Not a valid version string: #{version}" unless valid?
|
344
|
+
end
|
345
|
+
|
346
|
+
# Set the major, minor, and patch parts of this Semver
|
347
|
+
# @param match_data [MatchData] the match data from the version string
|
348
|
+
# @return [void]
|
349
|
+
# @api private
|
350
|
+
def core_parts(match_data)
|
351
|
+
@major = match_data[:major]
|
352
|
+
@minor = match_data[:minor]
|
353
|
+
@patch = match_data[:patch]
|
354
|
+
end
|
355
|
+
|
356
|
+
# Set the pre-release of this Semver
|
357
|
+
# @param match_data [MatchData] the match data from the version string
|
358
|
+
# @return [void]
|
359
|
+
# @api private
|
360
|
+
def pre_release_part(match_data)
|
361
|
+
@pre_release = match_data[:pre_release] || ''
|
362
|
+
@pre_release_identifiers = @pre_release.split('.').map { |f| f =~ /\A\d+\z/ ? f.to_i : f }
|
363
|
+
end
|
364
|
+
|
365
|
+
# Set the build_metadata of this Semver
|
366
|
+
# @param match_data [MatchData] the match data from the version string
|
367
|
+
# @return [void]
|
368
|
+
# @api private
|
369
|
+
def build_metadata_part(match_data)
|
370
|
+
@build_metadata = match_data[:build_metadata] || ''
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
data/lib/version_boss.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'version_boss/gem'
|
4
|
+
require_relative 'version_boss/semver'
|
5
|
+
|
6
|
+
# Parse, compare, and increment versions according to the SemVer 2.0.0
|
7
|
+
module VersionBoss
|
8
|
+
# Errors reported by the VersionBoss gem
|
9
|
+
class Error < StandardError; end
|
10
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/version_boss/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'version_boss'
|
7
|
+
spec.version = VersionBoss::VERSION
|
8
|
+
spec.authors = ['James Couball']
|
9
|
+
spec.email = ['jcouball@yahoo.com']
|
10
|
+
|
11
|
+
spec.summary = 'A Gem to parse and compare SemVer versions AND increment versions for Ruby Gems'
|
12
|
+
spec.description = <<~DESC
|
13
|
+
Parse, compare, and increment RubyGem versions with the 'gem-version' CLI
|
14
|
+
command or the 'VersionBoss::Gem::Version' class.
|
15
|
+
DESC
|
16
|
+
|
17
|
+
spec.homepage = 'http://github.com/main-branch/version_boss'
|
18
|
+
spec.license = 'MIT'
|
19
|
+
spec.required_ruby_version = '>= 3.0.0'
|
20
|
+
|
21
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
22
|
+
|
23
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
24
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
25
|
+
spec.metadata['changelog_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}/file/CHANGELOG.md"
|
26
|
+
spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/#{spec.name}/#{spec.version}"
|
27
|
+
|
28
|
+
# Specify which files should be added to the gem when it is released.
|
29
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
30
|
+
spec.files = Dir.chdir(__dir__) do
|
31
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
32
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)})
|
33
|
+
end
|
34
|
+
end
|
35
|
+
spec.bindir = 'exe'
|
36
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ['lib']
|
38
|
+
|
39
|
+
spec.add_runtime_dependency 'thor', '~> 1.3'
|
40
|
+
|
41
|
+
spec.add_development_dependency 'bundler-audit', '~> 0.9'
|
42
|
+
spec.add_development_dependency 'create_github_release', '~> 1.3'
|
43
|
+
spec.add_development_dependency 'rake', '~> 13.1'
|
44
|
+
spec.add_development_dependency 'rspec', '~> 3.12'
|
45
|
+
spec.add_development_dependency 'rubocop', '~> 1.59'
|
46
|
+
spec.add_development_dependency 'simplecov', '~> 0.22'
|
47
|
+
spec.add_development_dependency 'simplecov-lcov', '~> 0.8'
|
48
|
+
|
49
|
+
unless RUBY_PLATFORM == 'java'
|
50
|
+
spec.add_development_dependency 'redcarpet', '~> 3.6'
|
51
|
+
spec.add_development_dependency 'yard', '~> 0.9', '>= 0.9.28'
|
52
|
+
spec.add_development_dependency 'yardstick', '~> 0.9'
|
53
|
+
end
|
54
|
+
|
55
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
56
|
+
end
|